2013-5-6 训练赛总结
训练赛链接: http://www.acmore.net/contest.php?cid=1013
Problem A: Yard
题意: n*m的格子,有树与空地,要求每个位置上下左右树的数量为偶数.求拔除与种植最小花费.
解法: 状态压缩, 第一行状态确定后,则所有行都确定. 然后枚举第一行状态,取个最小值即可.
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> using namespace std; const int inf = 0x3f3f3f3f; char A[110][110]; int B[110][110]; int a[110][110]; int n, m, res; void gao(int &cnt){ int mark = 0; for(int i = 0; i < m; i++ ){ if( i == 0 ){ if( a[0][i+1] ) mark |= (1<<i); } else if( i == m-1 ){ if( a[0][i-1] ) mark |= (1<<i); } else{ mark |= ((a[0][i-1]+a[0][i+1])&1)<<i; } } for(int i = 1; i < n; i++ ){ // a[][] for(int j = 0; j < m; j++){ a[i][j] = mark&(1<<j) ? 1 : 0; if( a[i][j] != B[i][j] ){ if(++cnt > res) return; } } // status mark = 0; for(int j = 0; j < m; j++){ if( j == 0 ) mark |= ((a[i-1][j]+a[i][j+1])&1); else if(j == m-1) mark |= ((a[i][j-1]+a[i-1][j])&1)<<j; else mark |= ((a[i][j-1]+a[i-1][j]+a[i][j+1])&1)<<j; } } if( mark == 0 ) res = min( res, cnt ); } int solve(){ int Mark = (1<<m)-1 ; res = inf; for(int mark = 0; mark <= Mark; mark++){ int cnt = 0; for(int i = 0; i < m; i++){ a[0][i] = (mark&(1<<i))?1:0; cnt += (a[0][i]!=B[0][i]); } gao( cnt ); } printf("%d\n", res); } int main(){ while( scanf("%d%d",&m,&n) != EOF){ for(int i = 0; i < m; i++) scanf("%s", A[i] ); for(int i = 0; i < n; i++){ // col for(int j = 0; j < m; j++){ // row if( A[j][i] == '#' ) B[i][j] = 1; else B[i][j] = 0; } } solve(); } return 0; }
Problem B: ZZB的数学作业
题意: “把一个正整数M分成P个不超过K的正整数的和,满足分成的数不是N的倍数,并且P也不是N的倍数,求这样的P最小是多少?”
解法: 判定. 看了AC代码觉得好神奇. 有些地方不太明白为什么可以这样判定,感脚不严谨.,,,先贴份AC代码吧.
#include<cstdio> typedef long long LL; LL n, m, k; int main(){ while( scanf("%lld%lld%lld", &n,&m,&k) != EOF){ if(n == 1){ printf("-1\n"); continue; } if((n==2) && (m%2==0) ){ printf("-1\n"); continue; } if((m%n==0) && (k==1) ){ printf("-1\n"); continue; } k = k>m?m:k; while( k%n == 0 ) k--; LL cnt = m/k; LL left = m%k; if( left == 0 ){ if(cnt%n == 0) cnt++; } else{ cnt++; if(left%n == 0){ if( (k-1)%n == 0 ) cnt++; } if(cnt%n == 0) cnt++; } printf("%lld\n", cnt); } return 0; }
Problem C: 爱情测试
题意: 给出前三项a1,a2,a3,以及要求的项的编号n,并且数列{an}只可能是等差数列或者是首项为1的等比数列,要求A输出第n项模100007后的值。
解法: 判定下,然后根据等差或等比计算即可.
#include <iostream> #include <cmath> #include <cstring> #include <cstdio> #include <string> #include <stdlib.h> #include <algorithm> using namespace std; typedef long long LL; const LL Mod= 100007; LL a, b, c, n, d, q, ans; LL M_M( LL a, LL b ) { LL res=0, t=a; while( b ){ if( b&1 )res=(res+t)%Mod; t=(t+t)%Mod; b>>=1; } return res; } LL P_M( LL a, LL b ) { LL res=1, t=a; while( b ){ if( b&1 )res*=t, res%=Mod; t*=t, t%=Mod; b>>=1; } return res; } int cc( int d, int n ) { return (a+M_M(d, n-1))%Mod; } int bb( int q, int n ) { return (a*P_M( q, n-1 ))%Mod; } int main( ) { while( scanf("%lld%lld%lld%lld", &a, &b, &c, &n)==4 ){ if( a-b==b-c ){ d=b-a; ans=cc(d, n); } else{ q=b/a; ans=bb( q, n ); } printf("%lld\n", ans); } return 0; } /************************************************************** Problem: 1518 User: yefeng1627 Language: C++ Result: Accepted Time:0 ms Memory:1432 kb ****************************************************************/
Problem D: 除草
题意: 很长...
解法: 不会.
Problem E: 友谊圈(Easy)
题意: 求一个最长上升序列, 要求 ai -aj >= i-j .
解法: 因为有相对位置的影响, 预处理可以将 ai - i, 这样就消除了相对位置影响,就可以直接做一个最长上升子序列,即可. 注意 ai > 0.
因为这里 N = 1e5, 需要一个 O(NlogN)的算法, 通过保存当前长度最小, 然后二分查找即可,实现 NlogN.
有个地方需要注意的是, 因为我们令 ai-i,去掉了相对位置影响,则找LIS时,即可高度相同。所以 idx[m] <= x 。
#include<cstdio> const int N = 200010, inf = 0x3f3f3f3f; int n, ln, a[N], f[N] = {0,1}; int find(int x){ int l = 0, r = ln, m, res; while( l <= r ){ m = (l+r)>>1; //printf("l = %d, r = %d, m = %d\n", l, r, m ); if( f[m] <= x ) (res=m),(l=m+1); else r = m-1; //printf("l = %d, r = %d\n", l, r ); } return res; } int main(){ scanf("%d", &n); for(int i = 1; i <= n; i++){ scanf("%d", &a[i]); a[i] -= i; } f[0] = -inf; ln = 0; for(int i = 1; i <= n; i++) if( a[i] >= 0 ){ int t = find(a[i])+1; f[t] = a[i]; if(t > ln) ln = t; } printf("%d\n", ln ); return 0; } /************************************************************** Problem: 1523 User: yefeng1627 Language: C++ Result: Accepted Time:7 ms Memory:2476 kb ****************************************************************/
Problem F: [USACO 1.5.3]特殊的质数肋骨
题意: 找出长度为 N ,其前缀都为素数的所有数.
解法: 搜索, 过程中判定, 判定用开根号的方式.即可
#include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> using namespace std; int n; bool legal(int x){ if( x == 1 ) return false; for(int i = 2; i*i <= x; i++) if( x%i == 0 ) return false; return true; } void dfs(int t, int x){ if( t == n ){ printf("%d\n",x); return; } for(int i = 0; i <= 9; i++){ int tmp = x*10+i; if( legal(tmp) ) dfs( t+1, tmp ); } } int main(){ while( scanf("%d",&n) != EOF){ for(int i = 1; i <= 9; i++){ if( legal(i) ){ dfs( 1, i ); } } } return 0; } /************************************************************** Problem: 1075 User: yefeng1627 Language: C++ Result: Accepted Time:0 ms Memory:916 kb ****************************************************************/
Problem G: [USACO 1.4.4]母亲的牛奶
题意: 农民约翰有三个容量分别是A,B,C升的桶,A,B,C分别是三个从1到20的整数,最初,A和B桶都是空的,而C桶是装满牛奶的。有时,约翰把牛奶从一个桶倒到另一个桶中,直到被灌桶装满或原桶空了。当然每一次灌注都是完全的。由于节约,牛奶不会有丢失。写一个程序去帮助约翰找出当A桶是空的时候,C桶中牛奶所剩量的所有可能性。
解法: BFS搜索, 模型转换, {a,b,c},然后模拟转换过程,标记.
#include<cstdio> #include<cstring> #include<cstdlib> #include<queue> #include<algorithm> using namespace std; int A, B, C; bool vis[25][25][25]; struct node{ int a,b,c; }pre,nxt; queue<node>Q; void solve(){ pre.a = 0, pre.b = 0, pre.c = C; while( !Q.empty() ) Q.pop(); Q.push( pre ); vis[0][0][C] = true; while( !Q.empty() ){ pre = Q.front(); Q.pop(); int a = pre.a, b = pre.b, c = pre.c; if( a > 0 ){ if( b < B ){ nxt.a = a-min(a,(B-b)); nxt.b = b+min(a,(B-b)); nxt.c = c; if( !vis[nxt.a][nxt.b][nxt.c] ) Q.push( nxt ), vis[nxt.a][nxt.b][nxt.c] = true; } if( c < C ){ nxt.a = a-min(a,(C-c)); nxt.b = b; nxt.c = c+min(a,(C-c)); if( !vis[nxt.a][nxt.b][nxt.c] ) Q.push( nxt ), vis[nxt.a][nxt.b][nxt.c] = true; } } if( b > 0 ){ if( a < A ){ nxt.a = a+min(b,(A-a)); nxt.b = b-min(b,(A-a)); nxt.c = c; if( !vis[nxt.a][nxt.b][nxt.c] ) Q.push( nxt ), vis[nxt.a][nxt.b][nxt.c] = true; } if( c < C ){ nxt.a = a; nxt.b = b-min(b,(C-c)); nxt.c = c+min(b,(C-c)); if( !vis[nxt.a][nxt.b][nxt.c] ) Q.push( nxt ), vis[nxt.a][nxt.b][nxt.c] = true; } } if( c > 0 ){ if( a < A ){ nxt.a = a+min(c,(A-a)); nxt.b = b; nxt.c = c-min(c,(A-a)); if( !vis[nxt.a][nxt.b][nxt.c] ) Q.push( nxt ), vis[nxt.a][nxt.b][nxt.c] = true; } if( b < B ){ nxt.a = a; nxt.b = b+min(c,(B-b)); nxt.c = c-min(c,(B-b)); if( !vis[nxt.a][nxt.b][nxt.c] ) Q.push( nxt ), vis[nxt.a][nxt.b][nxt.c] = true; } } } } int main(){ while( scanf("%d%d%d",&A,&B,&C) != EOF){ memset( vis, 0, sizeof(vis) ); solve(); bool tmp[25]; memset(tmp,0,sizeof(tmp)); for(int b = 0; b <= B; b++) for(int c = 0; c <= C; c++) tmp[c] |= vis[0][b][c]; bool flag = false; for(int i = 0; i <= C; i++) if( tmp[i] ){ if( !flag ) printf("%d",i), flag =true; else printf(" %d",i); } puts(""); } return 0; } /************************************************************** Problem: 1074 User: yefeng1627 Language: C++ Result: Accepted Time:0 ms Memory:1032 kb ****************************************************************/
Problem H: [USACO 1.4.2]时钟
题意: 3*3的矩阵, 有10条规则,转换, 问最短的路径转换到要求状态.
解法: 还是一样BFS搜索, 使用标记. 处理有点麻烦.
#include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int N = (int)5e5+10; bool vis[4][4][4][4][4][4][4][4][4]; char mp[10]; bool flag; char b[9][9]; struct Node{ char p[10], kind; int f; }Q[N], cur; void init(){ memset(b,0,sizeof(b)); b[0][0]=b[0][1]=b[0][3]=b[0][4]=1; b[1][0]=b[1][1]=b[1][2]=1; b[2][1]=b[2][2]=b[2][4]=b[2][5]=1; b[3][0]=b[3][3]=b[3][6]=1; b[4][1]=b[4][3]=b[4][4]=b[4][5]=b[4][7]=1; b[5][2]=b[5][5]=b[5][8]=1; b[6][3]=b[6][4]=b[6][6]=b[6][7]=1; b[7][6]=b[7][7]=b[7][8]=1; b[8][4]=b[8][5]=b[8][7]=b[8][8]=1; } void gao(char *c ){ vis[c[0]][c[1]][c[2]][c[3]][c[4]][c[5]][c[6]][c[7]][c[8]] = true; } bool legal(char *c){ if(vis[c[0]][c[1]][c[2]][c[3]][c[4]][c[5]][c[6]][c[7]][c[8]]) return false; return true; } bool over(char *c){ if( vis[3][3][3][3][3][3][3][3][3] ) return true; return false; } int solve(){ memset( vis, 0, sizeof(vis)); int l = 0, r = 0; memcpy( Q[r].p, mp, 9*sizeof(char)); Q[r].f = -1; Q[r++].kind = -1; gao( mp ); while( l < r ){ cur = Q[l]; if( over(cur.p) ) return l; for(int i = 0; i < 9; i++){ Node tmp = cur; for(int j = 0; j < 9; j++){ tmp.p[j] = (tmp.p[j]+b[i][j])%4; } if( legal(tmp.p) ){ gao( tmp.p ); tmp.f = l; tmp.kind = i+1; Q[r++] = tmp; if( over(tmp.p) ) return r-1; } } l++; } } void output(int d){ if( Q[d].f == -1 ) return; output( Q[d].f ); if( flag ) flag = false, printf("%d",Q[d].kind); else printf(" %d", Q[d].kind ); } int main(){ init(); while( scanf("%d%d%d",&mp[0],&mp[1],&mp[2]) != EOF){ scanf("%d%d%d",&mp[3],&mp[4],&mp[5]); scanf("%d%d%d",&mp[6],&mp[7],&mp[8]); for(int i = 0; i < 9; i++) mp[i] = mp[i]/3 - 1; int d = solve(); flag = true; output(d); puts(""); } return 0; } /************************************************************** Problem: 1072 User: yefeng1627 Language: C++ Result: Accepted Time:85 ms Memory:8984 kb ****************************************************************/