(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。輸入格式 每行包含乙個正整數...