题目描述:HDU3722
Jimmy invents an interesting card game. There are N cards, each of which contains a string Si. Jimmy wants to stick them into several circles, and each card belongs to one circle exactly. When sticking two cards, Jimmy will get a score. The score of sticking two cards is the longest common prefix of the second card and the reverse of the first card. For example, if Jimmy sticks the card S1 containing "abcd" in front of the card S2 containing "dcab", the score is 2. And if Jimmy sticks S2 in front of S1, the score is 0. The card can also stick to itself to form a self-circle, whose score is 0.
For example, there are 3 cards, whose strings are S1="ab", S2="bcc", S3="ccb". There are 6 possible sticking:
1. S1->S2, S2->S3, S3->S1, the score is 1+3+0 = 4
2. S1->S2, S2->S1, S3->S3, the score is 1+0+0 = 1
3. S1->S3, S3->S1, S2->S2, the score is 0+0+0 = 0
4. S1->S3, S3->S2, S2->S1, the score is 0+3+0 = 3
5. S1->S1, S2->S2, S3->S3, the score is 0+0+0 = 0
6. S1->S1, S2->S3, S3->S2, the score is 0+3+3 = 6
So the best score is 6.
Given the information of all the cards, please help Jimmy find the best possible score.
For example, there are 3 cards, whose strings are S1="ab", S2="bcc", S3="ccb". There are 6 possible sticking:
1. S1->S2, S2->S3, S3->S1, the score is 1+3+0 = 4
2. S1->S2, S2->S1, S3->S3, the score is 1+0+0 = 1
3. S1->S3, S3->S1, S2->S2, the score is 0+0+0 = 0
4. S1->S3, S3->S2, S2->S1, the score is 0+3+0 = 3
5. S1->S1, S2->S2, S3->S3, the score is 0+0+0 = 0
6. S1->S1, S2->S3, S3->S2, the score is 0+3+3 = 6
So the best score is 6.
Given the information of all the cards, please help Jimmy find the best possible score.
InputThere are several test cases. The first line of each test case contains an integer N (1 <= N <= 200). Each of the next N lines contains a string Si. You can assume the strings contain alphabets ('a'-'z', 'A'-'Z') only, and the length of every string is no more than 1000.
OutputOutput one line for each test case, indicating the corresponding answer.
第一种写法:不用slack数组,寻找增广路径时使用DFS
#include <iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> using namespace std; #define MAXN 205 #define MAXL 1005 #define INF 0x7f7f7f7f int vy[MAXN],linky[MAXN],dis[MAXN][MAXN],wx[MAXN],wy[MAXN],n,ans,minz; char s[MAXN][MAXL],tmp[MAXL]; bool dfs(int s) { for(int i=1;i<=n;i++) { if(!vy[i]) { int t=wx[s]+wy[i]-dis[s][i]; if(t==0) { vy[i]=1; if(!linky[i]||dfs(linky[i])) { linky[i]=s; return true; } } else { if(minz>t) minz=t; } } } return false; } void km() { memset(linky,0,sizeof linky); memset(wy,0,sizeof wy); for(int i=1;i<=n;i++) { while(1) { memset(vy,0,sizeof vy); minz=INF; if(dfs(i))break; wx[i]-=minz; for(int j=1;j<=n;j++) { if(vy[j]) { wx[linky[j]]-=minz; wy[j]+=minz; } } } } } int main() { while(~scanf("%d",&n)) { memset(wx,0,sizeof wx); for(int i=1;i<=n;i++) scanf("%s",s[i]); for(int i=1;i<=n;i++) { int len=strlen(s[i]); strcpy(tmp,s[i]); for(int j=0;j<len/2;j++) swap(tmp[j],tmp[len-1-j]); int jj=0; for(int j=1;j<=n;j++) { if(j==i)continue; jj=0; while(jj<len&&tmp[jj]==s[j][jj])jj++; dis[i][j]=jj; if(wx[i]<dis[i][j])wx[i]=dis[i][j]; } } km(); ans=0; for(int i=1;i<=n;i++) ans+=dis[linky[i]][i]; printf("%d\n",ans); } return 0; }
第二种写法:增加slack数组,增广路径仍然采用dfs。这种写法其实和第一种写法的差不多,不会有时间复杂度上的变化。
#include <iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> using namespace std; #define MAXN 205 #define MAXL 1005 #define INF 0x7f7f7f7f int vy[MAXN],linky[MAXN],dis[MAXN][MAXN],wx[MAXN],wy[MAXN],n,ans,minz,slack[MAXN]; char s[MAXN][MAXL],tmp[MAXL]; bool dfs(int s) { for(int i=1;i<=n;i++) { if(!vy[i]) { int t=wx[s]+wy[i]-dis[s][i]; if(t==0) { vy[i]=1; if(!linky[i]||dfs(linky[i])) { linky[i]=s; return true; } } else { if(t<slack[i]) slack[i]=t; } } } return false; } void km() { memset(linky,0,sizeof linky); for(int i=1;i<=n;i++) {memset(slack,0x7f,sizeof slack); while(1) { memset(vy,0,sizeof vy); minz=INF; if(dfs(i))break; for(int j=1;j<=n;j++) if(!vy[j]) if(minz>slack[j])minz=slack[j]; wx[i]-=minz; for(int j=1;j<=n;j++) { if(vy[j]) { wx[linky[j]]-=minz; wy[j]+=minz; } else slack[j]-=minz; } } } } int main() { while(~scanf("%d",&n)) { memset(wx,0,sizeof wx); for(int i=1;i<=n;i++) scanf("%s",s[i]); for(int i=1;i<=n;i++) { int len=strlen(s[i]); strcpy(tmp,s[i]); for(int j=0;j<len/2;j++) swap(tmp[j],tmp[len-1-j]); int jj=0; for(int j=1;j<=n;j++) { if(j==i)continue; jj=0; while(jj<len&&tmp[jj]==s[j][jj])jj++; dis[i][j]=jj; if(wx[i]<dis[i][j])wx[i]=dis[i][j]; } } km(); ans=0; for(int i=1;i<=n;i++) ans+=dis[linky[i]][i]; printf("%d\n",ans); } return 0; }
第三种写法:增加slack数组,增广路径不是采用dfs了。这种写法才是真正的O(N^3)的。
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<cstdlib> #include<algorithm> using namespace std; #define MAXN 205 int n,dis[MAXN][MAXN],vx[MAXN],vy[MAXN],linky[MAXN],dbx[MAXN],dby[MAXN],slack[MAXN],pre[MAXN],ans; char s[MAXN][1005],tmp[1005],delta; void bfs(int k) { int py=0,px,yy=0,delta; linky[py]=k; memset(slack,0x7f,sizeof slack); memset(pre,0,sizeof pre); do { px=linky[py]; delta=0x7f7f7f7f; vy[py]=1; for(int i=1;i<=n;i++) { if(!vy[i]) {if(dbx[px]+dby[i]-dis[px][i]<slack[i]) {slack[i]=dbx[px]+dby[i]-dis[px][i]; pre[i]=py; } if(slack[i]<delta) {delta=slack[i]; yy=i; } } } for(int i=0;i<=n;i++) { if(vy[i]) { dbx[linky[i]]-=delta; dby[i]+=delta; } else slack[i]-=delta; } py=yy; }while(linky[py]!=0); while(py) { linky[py]=linky[pre[py]]; py=pre[py]; } } void km() { memset(linky,0,sizeof linky); for(int i=1;i<=n;i++) { memset(vy,0,sizeof vy); bfs(i); } } int main() { while(~scanf("%d",&n)) { ans=0; memset(dbx,0,sizeof dbx); memset(dby,0,sizeof dby); memset(dis,0,sizeof dis); for(int i=1;i<=n;i++) scanf("%s",s[i]); for(int i=1;i<=n;i++) { strcpy(tmp,s[i]); int len=strlen(s[i]); for(int j=0;j<len/2;j++) swap(tmp[j],tmp[len-1-j]); for(int j=1;j<=n;j++) { if(j==i)dis[i][j]=0; else { int jj=0; for(jj=0;jj<len&&tmp[jj]==s[j][jj];jj++); dis[i][j]=jj; dbx[i]=max(dbx[i],dis[i][j]); } } } km(); for(int i=1;i<=n;i++) { ans+=dis[linky[i]][i]; } printf("%d\n",ans); } }
但是在随机数据下,这三种写法都差不多。甚至第三种写法还稍慢一点。但是只是常数上稍大一些。而如果遇到这种数据边权为w[i][j]=i*j,则前两种就慢的多了。比如n=400左右的数据,前两种写法都要14s左右才能出来结果。而第三种写法只需要0.3s左右。