2014東莞市選 分組

2021-08-14 21:56:43 字數 3447 閱讀 6918

(file io): input:group.in output:group.out

time limits: 1000 ms memory limits: 262144 kb detailed limits special judge

description

有n個字串,給這些字串分組,使得每個字串屬於且僅屬於乙個組。

對於乙個合法的分組,至少滿足以下兩個條件種的乙個:

1. 所有字串的k字首相同(即前k個字母相同)

2. 所有字串的k字尾相同(即後k個字母相同)

你需要給這些字串分組,使得所分的組數最少。

input

第一行兩個整數n,k(1<=n<=5000, 1<=k<=550),分別表示字串的數量以及題述中的引數k。

接下來有n行,每行乙個字串,字串的長度至少為k,且不會超過550。

output

第一行乙個整數m,表示最少的分組數目。

接下來m行,每行的第乙個整數ti表示第i個分組的字串數量,接下來有ti個整數,表示第i個分組中的字串編號,編號對應字串的輸入順序。數字之間用乙個空格隔開。如果分組方案不唯一,輸出任意一種即可。

sample input

4 1

aaab

bbba

sample output

2

2 1 2

2 3 4

data constraint50%的資料n<=100

100%的資料n<=5000,k<=550

最開始的思路:

將串們排序,將k位的字首與字尾編號。

貪心,若某字首有分組,那麼塞進去,否則塞進字尾的分組,再否則新建分組,然而顯然是錯的:

4 2

aabc

adbc

adxy

aaxx

這樣會將1,2分組,導致不是最優

於是我考慮按照字首的順序加入,也是錯的:

4 2

aabc

aabd

ccbd

cdbc

同理。

於是我按照字尾的順序重做,取最小值,當然還是錯的!

——只是坑到了90+分

正解:假設單詞字首集合為

,字尾的集合為

,每個單詞可以用ai

bj表示,於是這就符合二分圖的定義了。

網路流。源點連向所有ai

,權為1,割掉這條邊表示選擇這個字首,所有bi

連向匯點,權為1,割掉表明選擇這個字尾,每個單詞為一條邊,權為∞,若st連通,則說明還有單詞沒被選到,答案就是最小割。

要找到組內的元素,就要知道割掉哪些邊,那麼怎麼找割邊呢?

想到殘餘網路中割邊權為0,但權為0時一定是割邊嗎?不是的:

s到t有兩條路徑,但只能跑乙個單位的流,圖中有兩條權為0的邊,但明顯僅有連向t的邊是割邊。

那麼就換一種方式判斷是不是割邊:從s開跑,如果能到達的點是靠t那一邊的bi

,在流完的情況下是不可能到匯點的,那麼bi

一定被割掉,反之不被割掉(最小割的情況下不可能再多割一條邊),如果能到達靠s那一邊的ai

,那麼ai

一定不被割掉,①直接連邊②上圖的情況,通過反向弧到達,反之若不能到達ai

,則一定被割掉。

這樣一來問題就迎刃而解了。

#include

#include

#include

#define n 5010

#define l 560

#define min(a,b) (ausing namespace std;

int n,k,cnt[2],ex[2][n],no[n][2],top=1,fir[n*2],las[n*2],nex[4

*n],to[4

*n],v[4

*n];

int own[2

*n],head[2

*n],nxt[2

*n],tot,s,t,h[n*2],vh[n*2],sta[n],que[n*2],h;

bool vis[n],used[n];

struct strpre[n],suf[n];

char c[l];

bool sm(str a,str b)return0;}

bool eq(str a,str b)

void link(int

x,int

y,int val)

void qlink(int

x,int

y)int flow(int

x,int fl)if(h[s]>t)return

0; }mn=min(mn,h[y]+1);

}if(!--vh[h[x]])h[s]=t+10;

++vh[h[x]=mn];return0;}

int main()sort(pre+1,pre+n+1,sm);sort(suf+1,suf+n+1,sm);

for(int i=1;i<=n;i++)no[pre[i].p][0]=(i==1 || !eq(pre[i],pre[i-1]))?++cnt[0]:cnt[0];

for(int i=1;i<=n;i++)no[suf[i].p][1]=(i==1 || !eq(suf[i],suf[i-1]))?++cnt[1]:cnt[1];

s=cnt[0]+cnt[1]+1;t=s+1;

for(int i=1;i<=cnt[0];i++)link(s,i,1),link(i,s,0);

for(int i=1;i<=cnt[1];i++)link(i+cnt[0],t,1),link(t,cnt[0]+i,0);

for(int i=1;i<=n;i++)

int tot=0;vh[0]=t;while(h[s]<=t)tot+=flow(s,n+n);

printf("%d",tot);

for(h=0,vis[que[t=1]=s]=1;h^t;)

for(int

x=que[++h],i=fir[x],y;i;i=nex[i])

if(v[i] && !vis[y=to[i]])vis[que[++t]=y]=1;

for(int i=1;i<=cnt[0];i++)if(!vis[i])

}for(int i=cnt[0]+1;i<=cnt[0]+cnt[1];i++)if(vis[i])

}fclose(stdin);fclose(stdout);

return

0;}

東莞市選2008 飛彈

這題的n很小。我們可以先用floyd求出兩兩之間的最小距離。然後要求最值,我們可以二分最長的距離。然後用匈牙利來判斷。將小於等於mid的邊新增 上標 include include include using namespace std struct nodee 10010 int k,n,m,f ...

東莞市選 格鬥俱樂部(區間dp)

輸出 輸出包含n行,每行為乙個整數 0 或 1 1 表示第i號選手有可能成為冠軍,0 表示不可能。輸入輸出樣例1 data.in 3 0 1 1 0 0 1 0 0 0 data.out 1 0 0區間dp嘛,狀態轉移也好想,設 f i j k 表示第 i 個人到第 j 個人打完時第 k 個人能不能...

2023年東莞市GDOI市選題第一題

第一題 數列 提交檔案 sequence.pas c cpp 輸入檔案 sequence.in 輸出檔案 sequence.out 問題描述 把乙個正整數分成一列連續的正整數之和。這個數列必須包含至少兩個正整數。你需要求出這個數列的最小長度。如果這個數列不存在則輸出 1。輸入格式 每行包含乙個正整數...