2015 China Collegiate Programming Contest

来源:互联网 发布:像一世之尊的小说知乎 编辑:程序博客网 时间:2024/05/21 17:23

唉!弱渣都没机会去比赛!

//代码还没提交过,不知道对不对,等题目挂出来再交。(已提交)

<题目连接(pdf)>

<提交地址>: uestc online judge

<提交地址>:hdu

A.Secrete Master Plan 

题意:给定2*2的矩阵A和B,问A是否能通过旋转得到B?

分析:模拟,把A转几下?

代码:

#include <bits/stdc++.h>using namespace std;typedef long long LL;typedef unsigned long long ULL;const LL INF = 1E9+9;int a[10],b[10],temp[10];void change(){temp[3]=a[1];temp[4]=a[3];temp[2]=a[4];temp[1]=a[2];a[1]=temp[1];a[2]=temp[2];a[3]=temp[3];a[4]=temp[4];}bool check(int *t,int *f){if(t[1]==f[1] && t[2]==f[2] && t[3]==f[3] && t[4]==f[4])return true;return false;}int main(){int ncase,i;scanf("%d",&ncase);for(int T=1;T<=ncase;T++){scanf("%d%d%d%d",&a[1],&a[2],&a[3],&a[4]);scanf("%d%d%d%d",&b[1],&b[2],&b[3],&b[4]);for(i=1;i<=5;i++,change())if(check(a,b))break;printf("Case #%d: %s\n",T,i>5?"IMPOSSIBLE":"POSSIBLE");}return 0;}

C.The Battle of Chibi

题意:在长度为n的序列中,求长度为m的单调递增序列的个数?

分析:记dp[i][j]表示当前递增序列长度为i,最后一个数字的下标为j。用三层循环肯定不行。先把所有的数离散化,再用树状数组加快找下标在j前面,且值比a[j]小的长度为i-1的方案数。

代码:

#include <bits/stdc++.h>using namespace std;typedef long long LL;typedef unsigned long long ULL;const LL INF = 1E9+9;const int maxn = 1007;const LL mod = 1e9+7;int lowbit[maxn],a[maxn],f[maxn];LL tree[maxn],dp[maxn][maxn];void add(LL &a,LL b){a+=b;while(a>=mod)a-=mod;}void Init(){for(int i=1;i<maxn;i++)lowbit[i]=i&-i;}void update(int x,LL v){for(int i=x;i<maxn;i+=lowbit[i])add(tree[i],v);}LL query(int x){LL ret(0);for(int i=x;i>=1;i-=lowbit[i])add(ret,tree[i]);return ret;}int main(){Init();int ncase,i,j,n,m;scanf("%d",&ncase);for(int T=1;T<=ncase;T++){scanf("%d%d",&n,&m);for(i=0;i<n;i++){scanf("%d",&a[i]);f[i]=a[i];}sort(f,f+n);int tail=unique(f,f+n)-f;for(i=0;i<n;i++)a[i]=lower_bound(f,f+tail,a[i])-f+1;for(i=0;i<n;i++)dp[1][i]=1;for(i=2;i<=m;i++){memset(tree,0,sizeof(tree));for(j=0;j<n;j++){dp[i][j]=query(a[j]-1);//printf("q:%d %lld\n",a[j]-1,query(a[j]-1)); update(a[j],dp[i-1][j]);}}LL ans=0;for(i=0;i<n;i++)add(ans,dp[m][i]);printf("Case #%d: %lld\n",T,ans);}return 0;}

D.Pick The Sticks

题意:从n个金条里面选出一些来,放在一条长为L的线段上(不能重叠,并且保证金条的中心在线段上),使价值最大。问最大价值?

分析:假如金条必须完完全全放在线段上,那么这个题就是01背包了。但是这个题是允许最多两个金条可以只有一半放在线段上。记dp[i][j][0/1/2],表示处理了前i个金条,容量为j,有0/1/2个金条只有一半在线段上的最大价值。那么对于一个金条,有三种选择,不选、全部放在线段上和一半放在线段上。

代码:

</pre><pre name="code" class="cpp">#include <bits/stdc++.h>using namespace std;typedef long long LL;typedef unsigned long long ULL;const LL INF = 1E9+9;LL dp[2][4007][3];int v[1007],w[1007];void Max(LL &ans,LL &a){ans=(ans>a?ans:a);}int main(){int ncase,i,j,k;int L,n;scanf("%d",&ncase);for(int T=1;T<=ncase;T++){scanf("%d%d",&n,&L);L<<=1;LL ans=0;for(i=1;i<=n;i++){scanf("%d%d",&w[i],&v[i]);w[i]<<=1;ans=ans>v[i]?ans:v[i];}int cur=0;memset(dp,0,sizeof(dp));for(i=1;i<=n;i++){cur^=1;for(j=0;j<=L;j++)for(k=0;k<3;k++)dp[cur][j][k]=dp[cur^1][j][k];for(j=L;j>=w[i]/2;j--){for(k=0;k<3;k++){if(j>=w[i])dp[cur][j][k]=max(dp[cur][j][k],dp[cur^1][j-w[i]][k]+v[i]);if(k && j>=w[i]/2)dp[cur][j][k]=max(dp[cur][j][k],dp[cur^1][j-w[i]/2][k-1]+v[i]);Max(ans,dp[cur][j][k]);}}}printf("Case #%d: %lld\n",T,ans);}return 0;}


E.ba Gua Zhen

题意:在一个连通图上找一个回路(从起点出发回到起点),使得回路上的所有经过的路径上的权值异或和最大。(如果一条路径走了x次,结果要异或x下)。

分析:①先把所有独立的回路的异或和找出来。②那么问题就转变为在n个数里面选任意多的数使得异或和最大。

对于①用dfs搜,搜的过程中记录当前位置由哪里来,使得不往回走。如果一个点被重复访问,说明形成回路。

对于②高斯消元

代码:

#include <bits/stdc++.h>using namespace std;typedef long long LL;typedef unsigned long long ULL;const LL INF = 1E9+9;const int maxn = 1e5+7;struct node{int id,next;LL w;}List[maxn<<4];int head[maxn],cnt;void Clear(){cnt=0;memset(head,-1,sizeof(head));}void add(int u,int v,LL w) // u-->v{List[cnt].id=v;List[cnt].w=w;List[cnt].next=head[u];head[u]=cnt++;}LL XOR[maxn<<4],num,buf[maxn];//buf[i]表示从起点到i的异或和 bool visit[maxn];void dfs(int cur,int per,LL value){visit[cur]=true;for(int i=head[cur];~i;i=List[i].next) if(List[i].id!=per){int to=List[i].id;LL w=List[i].w;if(visit[to]){XOR[num++]=value^w^buf[to];continue ;}buf[to]=value^w;dfs(to,cur,value^w);}}LL base[65];LL guass(int n){memset(base,0,sizeof(base));for(int i=0;i<n;i++){LL cur=XOR[i];for(int j=59;j>=0;j--){if((1ll<<j)&cur){if(base[j]==0){base[j]=cur;break;}else{cur^=base[j];}}}}LL ret=0;for(int i=0;i<60;i++)for(int j=i+1;j<60;j++)if((base[j]>>i)&1)base[j]^=base[i];for(int i=59;i>=0;i--)ret^=base[i];return ret;}int main(){int nCase,i,j,u,v,n,m;scanf("%d",&nCase);for(int T=1;T<=nCase;T++){scanf("%d%d",&n,&m);Clear();for(i=1;i<=m;i++){LL w;scanf("%d %d %lld",&u,&v,&w);add(u,v,w);add(v,u,w);}num=0;memset(visit,0,sizeof(visit));memset(buf,0,sizeof(buf));dfs(1,-1,0);printf("Case #%d: %lld\n",T,guass(num));}return 0;}                                                                                                                                     


G.Ancient Go

题意:给定一副围棋的局面,先将所有的已经死掉的'x'去掉,再将所有的已经死掉的'o'去掉,问在添加一个'x'是否能吃掉至少一个‘o’?

分析:先算出'x'的连通块边缘有多少个'.'与之相连,如果是0个就把这个连通块去掉,同理去掉‘o’。然后再算‘o’的连通块边缘有多少个'.'与之相连,若存在一个为1的,就满足条件,否则不满足。

代码:


#include <bits/stdc++.h>using namespace std;typedef long long LL;typedef unsigned long long ULL;const LL INF = 1E9+9;char maze[12][12];set <int > st;bool visit[100][100];void Find(int x,int y,char ch){if(visit[x][y])return ;visit[x][y]=1;//printf("(%d,%d)\n",x,y);if(maze[x+1][y]=='.')st.insert((x+1)*10+y);else if(maze[x+1][y]==ch)Find(x+1,y,ch);if(maze[x][y+1]=='.')st.insert((x)*10+y+1);else if(maze[x][y+1]==ch)Find(x,y+1,ch);if(maze[x-1][y]=='.')st.insert((x-1)*10+y);else if(maze[x-1][y]==ch)Find(x-1,y,ch);if(maze[x][y-1]=='.')st.insert(x*10+y-1);else if(maze[x][y-1]==ch)Find(x,y-1,ch);}void Delete(int x,int y,char ch){maze[x][y]='.';if(maze[x+1][y]==ch)Delete(x+1,y,ch);if(maze[x][y+1]==ch)Delete(x,y+1,ch);if(maze[x-1][y]==ch)Delete(x-1,y,ch);if(maze[x][y-1]==ch)Delete(x,y-1,ch);}void check(){int i,j;printf("-------------------------------------------\n");for(i=1;i<=9;i++){for(j=1;j<=9;j++){printf("%c",maze[i][j]);}printf("\n");}printf("\n");}int main(){//freopen("test.txt","r",stdin);//freopen("ou.txt","w",stdout);int ncase,n,m,i,j;scanf("%d",&ncase);for(int T=1;T<=ncase;T++){for(i=1;i<=9;i++)scanf("%s",maze[i]+1);memset(visit,0,sizeof(visit));for(i=1;i<=9;i++){for(j=1;j<=9;j++){if(maze[i][j]=='o' && !visit[i][j]){st.clear();Find(i,j,'o');if(st.size()==0)Delete(i,j,'o');}}}//check();memset(visit,0,sizeof(visit));for(i=1;i<=9;i++){for(j=1;j<=9;j++){if(maze[i][j]=='x' && !visit[i][j]){st.clear();Find(i,j,'x');if(st.size()==0)Delete(i,j,'x');}}}//check();memset(visit,0,sizeof(visit));bool ok=false;for(i=1;i<=9 && !ok;i++){for(j=1;j<=9 && !ok;j++){if(maze[i][j]=='o' && !visit[i][j]){st.clear();Find(i,j,'o');if(st.size()==1)ok=true;}}}printf("Case #%d: %s\n",T,ok?"Can kill in one move!!!":"Can not kill in one move!!!");}return 0;}


H.Sudoku

题意:将4*4的矩阵上空缺的位置填上数字,使其称为Sudoku。输出矩阵。

分析:把空缺的位置的坐标存起来,然后dfs暴力枚举空缺的位置上的数,判断填的数是否合法。能填满就说明方案可行。

代码:


#include <bits/stdc++.h>using namespace std;typedef long long LL;typedef unsigned long long ULL;const LL INF = 1E9+9;struct node{int x,y;}s[100];int cnt,getIt;char maze[10][10];bool ok(int x,int y,char ch){for(int i=1;i<=4;i++)if(maze[x][i]==ch)return false;for(int i=1;i<=4;i++)if(maze[i][y]==ch)return false;if(x<=2 && y<=2){if(maze[1][1]==ch || maze[1][2]==ch || maze[2][1]==ch || maze[2][2]==ch)return false;}if(x>2 && y<=2){if(maze[3][1]==ch || maze[3][2]==ch || maze[4][1]==ch || maze[4][2]==ch)return false;}if(x<=2 && y>2){if(maze[1][3]==ch || maze[1][4]==ch || maze[2][3]==ch || maze[2][4]==ch)return false; }if(x>2 && y>2){if(maze[3][3]==ch || maze[3][4]==ch || maze[4][3]==ch || maze[4][4]==ch)return false;}return true;}void DFS(int cur){if(getIt)return ;if(cur>=cnt){for(int i=1;i<=4;i++){for(int j=1;j<=4;j++)putchar(maze[i][j]);putchar('\n');}getIt=1;return ;}int nx=s[cur].x,ny=s[cur].y;char buf=maze[nx][ny];for(char ch='1';ch<='4';ch++) if(ok(nx,ny,ch)){maze[nx][ny]=ch;DFS(cur+1);maze[nx][ny]=buf;}}int main(){//freopen("data.txt","r",stdin);//freopen("out.txt","w",stdout);int ncase,i,j;scanf("%d",&ncase);for(int T=1;T<=ncase;T++){getIt=cnt=0;for(i=1;i<=4;i++){scanf("%s",maze[i]+1);for(j=1;j<=4;j++){if(maze[i][j]=='*'){s[cnt].x=i;s[cnt++].y=j;}}}printf("Case #%d:\n",T);DFS(0);}return 0;}


K.Game Rooms 

题意:有n层楼,现在每一层楼可以建两种设施里面的一种,已知每层楼里面分别想玩两种设施的人数,每个人的消耗为离他最近的楼层(楼层里面有他想玩的设施)的距离,现在问怎样建设施,使得总的消耗最小。

分析:定义dp[i][j][type],表示当前楼层为i,与之最近且不同的楼层为j(j<i),j建type设施。由当前楼层推到下一层的状态转移为:

 j               i i+1

$ * * * * * * *

①下一层也建type类型的设施,dp[i+1][j][type]=min(dp[i+1][j][type],dp[i][j][type]); //j~i+1的消耗暂时先不算

j                i i+1

$ * * * * * * $

②下一层建另外一种类型的设施:dp[i+1][i][type^1]=min(dp[i+1][i][type^1],dp[i][j][type]+v(j+1~i的消耗))

//j+1~i这一段的消耗=前一半到j层的消耗+后一半到i+1层的消耗

ps:消耗需要先预处理出来,O(1)查询。还有最后要把没有加完的部分加上。

特殊情况:********$,dp[i+1][i][type]=(前面所有*到$的消耗)

代码:

#include <bits/stdc++.h>using namespace std;typedef long long LL;typedef unsigned long long ULL;const LL INF = 1E18+9;const int maxn = 4007;LL dp[2][maxn][2];LL Prefix[maxn][2],Suffix[maxn][2],Sum[maxn][2];LL a[maxn],b[maxn],n;void Init(int n){int i,j,c;Prefix[0][0]=Prefix[0][1]=0;for(i=1;i<=n;i++){Prefix[i][0]=Prefix[i-1][0]+a[i]*i;Prefix[i][1]=Prefix[i-1][1]+b[i]*i;}Suffix[n+1][0]=Suffix[n+1][1]=0;for(i=n,c=1;i>=1;i--,c++){Suffix[i][0]=Suffix[i+1][0]+a[i]*c;Suffix[i][1]=Suffix[i+1][1]+b[i]*c;}}inline LL getPrefix(int type,int L,int R){if(L>R)return 0;LL ret=Prefix[R][type]-Prefix[L-1][type];returnret-(Sum[R][type]-Sum[L-1][type])*(L-1);}inline LL getSuffix(int type,int L,int R){if(L>R)return 0;LL ret=Suffix[L][type]-Suffix[R+1][type];return ret-(Sum[R][type]-Sum[L-1][type])*(n-R); }int main(){//freopen("test.txt","r",stdin);//freopen("oyx.txt","w",stdout);int ncase,i,j,k;scanf("%d",&ncase);for(int T=1;T<=ncase;T++){scanf("%d",&n);Sum[0][0]=Sum[0][1]=0;for(i=1;i<=n;i++){scanf("%lld%lld",&a[i],&b[i]);Sum[i][0]=Sum[i-1][0]+a[i];Sum[i][1]=Sum[i-1][1]+b[i];}Init(n);int cur=0;for(j=1;j<=n;j++)dp[cur][j][0]=dp[cur][j][1]=INF;dp[cur][1][0]=b[1]; //现在的cur表示第2层 ,处理了第cur-1=1层dp[cur][1][1]=a[1];for(i=2;i<n;i++){cur^=1;for(j=1;j<=i;j++)for(k=0;k<2;k++)dp[cur][j][k]=INF;dp[cur][i][0]=getSuffix(1,1,i);dp[cur][i][1]=getSuffix(0,1,i);for(j=1;j<i;j++){for(k=0;k<2;k++){LL v=dp[cur^1][j][k];if(v>=INF)continue ;dp[cur][j][k]=min(dp[cur][j][k],v);int mid=(j+1+i)>>1;LL temp=getPrefix(k,j+1,mid)+getSuffix(k,mid+1,i);dp[cur][i][k^1]=min(dp[cur][i][k^1],v+temp);}}}LL ans=INF,temp;for(j=1;j<n;j++){temp=getPrefix(0,j+1,n);    ans=min(ans,dp[cur][j][0]+temp);        temp=getPrefix(1,j+1,n);        ans=min(ans,dp[cur][j][1]+temp);}printf("Case #%d: %lld\n",T,ans);} return 0;}

L.Huatuo's Medicine

题意:Huatuo有很多药和很多相同的瓶子,huatuo把药瓶子串成一条链,现在他只知道每种药的位置(不知道从哪头开始),问假如有n种药,这条链最短多长他能准确的取出他想要的药?

分析:2*n-1?

代码:

#include <bits/stdc++.h>using namespace std;typedef long long LL;typedef unsigned long long ULL;const LL INF = 1E9+9;int main(){int ncase,x;scanf("%d",&ncase);for(int T=1;T<=ncase;T++){scanf("%d",&x);printf("Case #%d: %d\n",T,x*2-1);}return 0;}

0 0