2013金山西山居创意游戏程序挑战赛——复赛(1) HDU 4557 非诚勿扰 HDU 4558 剑侠情缘 HDU 4559 涂色游戏 HDU 4560 我是歌手
今天的比赛。。。。。速度还是慢了。。。
无缘无故错误提交好多次。。哭了。。。。
比较简单:
A题:HDU 4557
非诚勿扰
链接:http://acm.hdu.edu.cn/showproblem.php?pid=4557
暴力胡搞,水题:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//============================================================================ // Name : A.cpp // Author : // Version : // Copyright : Your copyright notice // Description : Hello World in C++, Ansi-style //============================================================================ #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #include <queue> #include <vector> #include <map> #include <set> #include <string> #include <math.h> using namespace std; const int MAXN=1010; char str[MAXN][100]; int a[MAXN]; bool used[MAXN]; int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); int T; int n; int top; scanf("%d",&T); int iCase=0; while(T--) { iCase++; printf("Case #%d:\n",iCase); scanf("%d",&n); char op[20]; memset(used,false,sizeof(used)); int num=0; top=0; while(n--) { scanf("%s",&op); if(op[0]=='A') { scanf("%s%d",&str[top],&a[top]); top++; num++; printf("%d\n",num); } else { int tmp; scanf("%d",&tmp); int k=-1; for(int i=0;i<top;i++) if(!used[i]&&a[i]>=tmp) if(k==-1||(a[i]<a[k])) { k=i; } if(k==-1)printf("WAIT...\n"); else { printf("%s\n",str[k]); used[k]=true; num--; } } } } return 0; }
B题:HDU 4558
剑侠情缘
链接:http://acm.hdu.edu.cn/showproblem.php?pid=4558
DP
一开始状态想错了。。只要记录差值就可以的
dp[i][j][x]表示在(i,j)这个点,差值为x
转移的时候把差值取相反数就实现轮流了
dp[i][j][x]表示(i,j)出发。人和剑的能量的差值为x.可以得到的相反数
那么状态转移的时候,首先x+=这个点的值。。如果这个时候x==0表示想到了,那么dp[i][j][x]++
然后往下和往右转移。下一步是更新剑了。所以对x取相反数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//============================================================================ // Name : B.cpp // Author : // Version : // Copyright : Your copyright notice // Description : Hello World in C++, Ansi-style //============================================================================ #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #include <queue> #include <vector> #include <map> #include <set> #include <string> #include <math.h> using namespace std; const int MAXN=500; char str[MAXN][MAXN]; const int MOD=1e9+7; int dp[MAXN][MAXN][11]; int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); int T; int n,m; scanf("%d",&T); int iCase=0; while(T--) { iCase++; printf("Case %d: ",iCase); scanf("%d%d",&n,&m); for(int i=0;i<n;i++)scanf("%s",str[i]); int ans=0; memset(dp,0,sizeof(dp)); for(int i=n-1;i>=0;i--) { for(int j=m-1;j>=0;j--) { for(int x=0;x<11;x++) { int tmp=x+(str[i][j]-'0'); tmp=(tmp)%11; int tmp2=(11-tmp)%11; if(i<n-1) { dp[i][j][x]+=dp[i+1][j][tmp2]; } dp[i][j][x]%=MOD; if(j<m-1)dp[i][j][x]+=dp[i][j+1][tmp2]; dp[i][j][x]%=MOD; if(tmp==0) { dp[i][j][x]++; dp[i][j][x]%=MOD; } if(x==0) { ans+=dp[i][j][x]; ans%=MOD; } } } } printf("%d\n",ans); } return 0; }
C:HDU 4559
涂色游戏
博弈题:
链接:http://acm.hdu.edu.cn/showproblem.php?pid=4559
SG函数计算。
SG函数很高深。。。
想了好久才会。
其实1*1的点sg值就是1
sg[i]表示2*i 的sg值。
sg[i]求解就是普通的SG函数了。
然后进行分段做。
分段;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//============================================================================ // Name : C.cpp // Author : // Version : // Copyright : Your copyright notice // Description : Hello World in C++, Ansi-style //============================================================================ #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #include <queue> #include <vector> #include <map> #include <set> #include <string> #include <math.h> using namespace std; const int MAXN=5000; int sg[MAXN]; bool vis[MAXN]; int mex(int x) { if(sg[x]!=-1)return sg[x]; memset(vis,false,sizeof(vis)); for(int i=0;x-1-i>=i;i++) { int tmp=mex(i)^mex(x-1-i)^1; vis[tmp]=true; } for(int i=0;x-2-i>=i;i++) { int tmp=mex(i)^mex(x-2-i); vis[tmp]=true; } for(int i=0;;i++) if(!vis[i]) { sg[x]=i; break; } return sg[x]; } bool g[2][MAXN]; int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); memset(sg,-1,sizeof(sg)); sg[0]=0; for(int i=1;i<5000;i++) sg[i]=mex(i); int T; scanf("%d",&T); int iCase=0; while(T--) { int n,m; scanf("%d%d",&n,&m); memset(g,false,sizeof(g)); int u,v; while(m--) { scanf("%d%d",&u,&v); u--;v--; g[u][v]=true; } int len=0; int ans=0; for(int i=0;i<n;i++) { if(g[0][i]||g[1][i]) { ans^=sg[len]; len=0; if(g[0][i]&&g[1][i])continue; ans^=1; } else len++; } ans^=sg[len]; iCase++; if(ans)printf("Case %d: Alice\n",iCase); else printf("Case %d: Bob\n",iCase); } return 0; }
D:HDU 4560
我是歌手
最大流+二分
链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4560
这个题目的建图比较常规。
N个歌手编号为1~N
M种歌曲,拆点。编号分别为N+1~N+M,N+M+1~N+2*M
源点为0,汇点为N+2*M+1 总共N+2*M+2个点。
N+i 和 N+M+i 之间连权值为K的边,作为限制。
对于i, j 如果i对j擅长,那么连边 i -> N+M+j
否则连边 i -> N+j 这样就可以限制了。
然后是二分答案。
0~(1~N)之间,连边 mid .
(N+M+1~N+2*M)与N+2*M+1之间也连边mid
然后判断mid*N和最大流是不是相当。
然后就是最大流模板题了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//============================================================================ // Name : D.cpp // Author : // Version : // Copyright : Your copyright notice // Description : Hello World in C++, Ansi-style //============================================================================ #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #include <queue> #include <vector> #include <map> #include <set> #include <string> #include <math.h> using namespace std; const int MAXN=1100; int maze[MAXN][MAXN]; int gap[MAXN],dis[MAXN],pre[MAXN],cur[MAXN]; int flow[MAXN][MAXN];//存最大流的容量 int sap(int start,int end,int nodenum) { memset(cur,0,sizeof(cur)); memset(dis,0,sizeof(dis)); memset(gap,0,sizeof(gap)); memset(flow,0,sizeof(flow)); int u=pre[start]=start,maxflow=0,aug=-1; gap[0]=nodenum; while(dis[start]<nodenum) { loop: for(int v=cur[u];v<nodenum;v++) if(maze[u][v]-flow[u][v] && dis[u]==dis[v]+1) { if(aug==-1 || aug>maze[u][v]-flow[u][v])aug=maze[u][v]-flow[u][v]; pre[v]=u; u=cur[u]=v; if(v==end) { maxflow+=aug; for(u=pre[u];v!=start;v=u,u=pre[u]) { flow[u][v]+=aug; flow[v][u]-=aug; } aug=-1; } goto loop; } int mindis=nodenum-1; for(int v=0;v<nodenum;v++) if(maze[u][v]-flow[u][v]&&mindis>dis[v]) { cur[u]=v; mindis=dis[v]; } if((--gap[dis[u]])==0)break; gap[dis[u]=mindis+1]++; u=pre[u]; } return maxflow; } bool used[MAXN][MAXN]; int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); int T; int N,M,L,K; scanf("%d",&T); int iCase=0; while(T--) { iCase++; scanf("%d%d%d%d",&N,&M,&L,&K); memset(maze,0,sizeof(maze)); int u,v; memset(used,false,sizeof(used)); while(L--) { scanf("%d%d",&u,&v); used[u][v]=true; } for(int i=N+1;i<=N+M;i++)maze[i][i+M]=K; for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) { if(used[i][j]) { maze[i][j+N+M]=1; } else maze[i][j+N]=1; } int ans=0; int l=0,r=M; while(l<=r) { int mid=(l+r)/2; for(int i=1;i<=N;i++)maze[0][i]=mid; for(int i=N+M+1;i<=N+2*M;i++)maze[i][N+2*M+1]=mid; if(sap(0,N+2*M+1,N+2*M+2)==mid*N) { ans=mid; l=mid+1; } else r=mid-1; } printf("Case %d: %d\n",iCase,ans); } return 0; }