hdu 4068 SanguoSHA
暴力题
选拔赛的题目当时想歪了也不敢下手做
题意:三国杀,我方和敌方都有n人,会给出我方武将的全部名字。然后下面n行,第i行先输入一个m,后面跟着m个名字,表示敌方第i个武将能克制我方的这m个武将,没有列出名字的武将则会克制敌方的第i个武将。两方对战,一方武将死了下一位补上,直到一方武将全部死亡则该方输掉了游戏。问你是否能构建出一种无敌的出场方式,无论敌方怎么派出武将,我方都必将取得胜利,如果不能输出No,能的话,输出Yes,另外如果有多种无敌序列,要按名字字典序输出(是指单个单个名字比较,而不是把全部名字压成一个字符串再比较)
由于数据规模很小n最大为6,所以其实是个水题,只要暴力枚举两方的全排列然后逐一比较即可
1.我方武将和敌方武将都从0到n-1标号。构建一个01矩阵g[i][j]=1,表示敌方的第i个武将能杀死我方的第i个武将,=0则表示不能杀死
2.枚举全排列用STL来写,但是注意用next_permutation()之前要先排序(或者你能保证一开始时是字典序最小序列),太久没用这个函数忘记排序WA了好几次
3.枚举全排列其实可以不用像代码中那样写,可以先把所有排列保存下来,以后直接用即可,可以节省时间,另外用dfs手打全排列也能提高时间,代码中为了方便就用STL
#include <cstdio> #include <cstring> #include <algorithm> #define LEN 25 #define N 10 using namespace std; bool g[N][N]; char name[N][LEN]; int n; int p1[N],p2[N]; void change(int *ans) { int x,y,OK=0; for(int i=0; i<n; i++) { x=ans[i]; y=p1[i]; if(strcmp(name[x],name[y]) < 0) break; else if(strcmp(name[y],name[x]) < 0) { OK=1; break; } } if(OK) for(int i=0; i<n; i++) ans[i]=p1[i]; } bool fight() { int i=0,j=0,x,y; while(i<n && j<n) { x=p1[i]; y=p2[j]; if(g[y][x]) i++; else j++; } return i<n; } void solve(int Case) { for(int i=0; i<n; i++) p1[i]=p2[i]=i; int ans[N],FIND,OK; FIND=0; do { sort(p2,p2+n); OK=1; do { if(!fight()) { OK=0; break; } } while(next_permutation(p2,p2+n)); if(OK) { if(!FIND) { FIND=1; for(int i=0; i<n; i++) ans[i]=p1[i]; } else change(ans); } } while(next_permutation(p1,p1+n)); if(!FIND) printf("Case %d: No\n",Case); else { printf("Case %d: Yes\n",Case); for(int i=0; i<n; i++) { if(i) printf(" "); printf("%s",name[ans[i]]); } printf("\n"); } } void input() { scanf("%d",&n); for(int i=0; i<n; i++) scanf("%s",name[i]); memset(g,0,sizeof(g)); for(int i=0; i<n; i++) { int m; char tmp[LEN]; scanf("%d",&m); for(int j=0; j<m; j++) { scanf("%s",tmp); for(int k=0; k<n; k++) if(!strcmp(name[k],tmp)) { g[i][k]=1; break; } } } } int main() { int T,t; scanf("%d",&T); for(t=1; t<=T; t++) { input(); solve(t); } return 0; }