NOIP2010 机器翻译 乌龟棋 关押罪犯
2017-03-10 17:33
609 查看
NOIP2010
第一题
题目较简单,可以直接模拟,used【】存内存中的数,如果溢出就删除。
#include <iostream> #include <cstdio> using namespace std; const int N = 1010; int used , a ; int tot, b ,j; int main(){ freopen("translate.in","r",stdin); freopen("translate.out","w",stdout); int n, m; scanf("%d%d", &n, &m); for(int i=1; i<=m; i++){ j++; scanf("%d", &a[j]); if(used[a[j]] == 0){ used[a[j]]++; tot++; b++; if(b > n) used[a[j-n]]--; if(used[a[j-n]] < 0) used[a[j-n]] = 0;//不允许减为负 } else j--; } printf("%d", tot); return 0; }
第二题
又是一道明显的dp题,但是针对这道题目特有的优化却不容易想到。关键就是要仔细分析数据范围(每种卡片的张数不超过四十!!!)这就是提示我们如何dp的最关键信息。因为四张卡片的用量一旦确定下来,乌龟所在的位置也就确定下来了。少一张几步的卡片就从几步之前的位置转移而来,状态转移的方程是很容易写的。所以四维的dp解决问题。
#include<iostream> #include<cstdio> #include <algorithm> using namespace std; int m,k,t; int c[150],f[41][41][41][41],v[355],sum[5]; int main() { freopen("tortoise.in","r",stdin); freopen("tortoise.out","w",stdout); scanf("%d%d",&t,&m); for(int i=1; i<=t; i++) scanf("%d",&v[i]); for(int i=1; i<=m; i++){ scanf("%d",&c[i]); sum[c[i]]++; } f[0][0][0][0] = v[1]; for(int i=0; i<=sum[1]; i++) for(int j=0; j<=sum[2]; j++) for(int k=0; k<=sum[3]; k++) for(int l=0; l<=sum[4]; l++){ int s = i + 2*j + 3*k + 4*l + 1; if(i > 0) f[i][j][k][l] = max(f[i][j][k][l], f[i-1][j][k][l] + v[s]); if(j > 0) f[i][j][k][l] = max(f[i][j][k][l], f[i][j-1][k][l] + v[s]); if(k > 0) f[i][j][k][l] = max(f[i][j][k][l], f[i][j][k-1][l] + v[s]); if(l > 0) f[i][j][k][l] = max(f[i][j][k][l], f[i][j][k][l-1] + v[s]); } printf("%d",f[sum[1]][sum[2]][sum[3]][sum[4]]); return 0; }
第三题
这是一道用并差集解决的题目,这里有一个最优的思想,就是要先处理权值高的遍,因为最终的结果由最大值决定,所以说如果大的遍不能解决,小的遍就没什么意义了。所以我们存完遍后,先排一次序,然后从大到小地模拟放点入栈的操作,直到产生矛盾,就取用最大值作为答案。还有就是如果在模拟的过程中遇见没法确定放入哪个栈的情况,就先不管它,打上一个标记,等有新的点被放入栈之后,再返回来判断。因为是在编的中途想到的并查集,下面的代码并不是标准的并查集,只是采用了并查集的思想。而且可能有一些瑕疵,仅供参考。
#include <iostream> #include <cstdio> #include <cstdlib> #include <ctime> #include <cstring> #include <algorithm> using namespace std; const int N = 100010; int fam[20010], used ; struct ln{ int sum; int p1, p2; }ed ; bool mycmp(ln a,ln b){ return a.sum > b.sum; } int main() { freopen("prison.in","r",stdin); freopen("prison.out","w",stdout); memset(fam, -1, sizeof(fam)); int n, m; scanf("%d%d", &n, &m); for(int i=1; i<=m; i++){ scanf("%d%d%d", &ed[i].p1, &ed[i].p2, &ed[i].sum); } sort(ed+1,ed+1+m,mycmp); fam[ed[1].p1] = 0; fam[ed[1].p2] = 1; used[1] = 1; int k = 2, nxt = 1, flag = 0; while(ed[k].sum){ int u1 = ed[k].p1; int u2 = ed[k].p2; if(fam[u1] == -1 && fam[u2] > -1 && used[k] == 0){ fam[u1] = (fam[u2] + 1) % 2; used[k] = 1; k = nxt-1; nxt = 1; } else if(fam[u2] == -1 && fam[u1] > -1 && used[k] == 0){ fam[u2] = (fam[u1] + 1) % 2; used[k] = 1; k = nxt-1; nxt = 1; } else if(fam[u2] == -1 && fam[u1] == -1 && nxt == 1){ nxt = k; } else if(fam[u2] > -1 && fam[u1] > -1 && used[k] == 0 && fam[u2] == fam[u1]){ printf("%d", ed[k].sum); flag = 1; break; } k++; } if(flag == 0) printf("%d", 0); return 0; }
第四题
用dfs实现本题是一个很容易想到的思路,但是有一个问题就是我们如何找出一个最优解呢?dfs可以帮助我们判断输出0 or 1(打标记),如果是1,我们就需要找最优解,那么简单的证明一下,由一个临水城市可以到达的沙漠城市一定是一条连续的线段,可以用反证法论证,如果不连续,这个断点就会由另一个临水城市到达,那么这两个临水城市覆盖的位置就一定会交叉,那么交叉点下面的城市(断点)就一定可以被这两个城市共同覆盖。于是就可以采用线段覆盖(类似dp)的方法快速求得最优。因为数据里有一个点会卡dfs,所以说经过反复尝试,用c提交+register不会T掉。
#include<string.h> #include<stdio.h> inline int readin(){ static char ch; static int res; while((ch=getchar())>'9'||ch<'0');res=ch-48; while((ch=getchar())>='0'&&ch<='9')res=res*10+ch-48; return res; } int n, m, num = 0; int vis[510][510], ans[510] ,pos[510][510], f[510]; struct ln{ int l,r; }ed[510]; int min(int a, int b){ return a<b?a:b; } int max(int a, int b){ return a>b?a:b; } void dfs(int x,int y,int st) { vis[x][y] = 1; if(x == m){ ans[y] = 1; ed[st].l = min(ed[st].l, y); ed[st].r = max(ed[st].r, y); } if(pos[x+1][y] < pos[x][y] && x != m && !vis[x+1][y]) dfs(x+1,y,st); if(pos[x-1][y] < pos[x][y] && x != 1 && !vis[x-1][y]) dfs(x-1,y,st); if(pos[x][y+1] < pos[x][y] && y != n && !vis[x][y+1]) dfs(x,y+1,st); if(pos[x][y-1] < pos[x][y] && y != 1 && !vis[x][y-1]) dfs(x,y-1,st); } int main() { freopen("flow.in","r",stdin); freopen("flow.out","w",stdout); m=readin(),n=readin(); for(register int i=1; i<=n; i++) ed[i].l = f[i] = 10000000; for(register int i=1; i<=m; i++) for(register int j=1; j<=n; j++) pos[i][j]=readin(); for(register int i=1; i<=n; i++){ memset(vis, 0, sizeof(vis)); dfs(1, i, i); } for(register int i=1; i<=n; i++) if(ans[i] == 0) num++; if(num > 0) { printf("%d\n%d", 0, num); return 0; } else{ printf("1\n"); for(register int i=1; i<=n; i++) for(register int j=1; j<=n; j++){ if(i >= ed[j].l && i <= ed[j].r) f[i] = min(f[i], f[ed[j].l - 1] + 1); } printf("%d", f ); } return 0; }
考试总结
一次10年noip真题练习。一般来说第一道题比较简单,这一次还没有摸清题目,所以说第一道题的耗时较长,原因就是把题目想的过于复杂,其实用常规方法10min就完全可以解决,这样就耽误了之后做题的时间。第二题是一个四维的dp,最开始想的是五维,没有想到简化的方法,这类题目其实难度并不大,但是需要想出巧一点的方法。第三题是一个并查集的题目,思想没有问题,不过代码不够规范,导致最后一个点没有调出来。最后一道是dp+区间覆盖,当时没有严格的证明区间的正确性,方法不太好想,其中有一个测试点要卡dfs,最后用c语言提交没有超时(不能用algorithm,加.h,不能在for中定义变量)。
相关文章推荐
- [NOIP2010]机器翻译,乌龟棋,关押罪犯,引水入城
- [noip模拟]四道题 noip2010 机器翻译 noip2010 乌龟棋 noip 关押罪犯 noip 引水入城
- NOIP2010提高组复赛 解题报告(C/C++)(机械翻译)(乌龟棋)(关押罪犯)(引水入城)
- Noip 2010 解题报告(机器翻译,乌龟棋,关押罪犯,引水入城)
- NOIP 2010 解题报告(机器翻译,乌龟棋,关押罪犯,引水入城)
- [noip2010]关押罪犯 并查集
- [NOIP2010 T3]关押罪犯
- 【NOIP2010】关押罪犯
- TYVJ 1403 [NOIP2010]关押罪犯
- NOIP2010 关押罪犯
- NOIP2010提高组 关押罪犯 -SilverN
- NOIP 2010 关押罪犯 并查集 二分+二分图染色
- noip2010 关押罪犯
- 【noip2010】关押罪犯
- [NOIP 2010]关押罪犯 贪心+并查集
- [NOIP2010] 关押罪犯
- 题解【luoguP1525 NOIp提高组2010 关押罪犯】
- 关押罪犯(noip2010)
- noip 2010 关押罪犯 (二分图染色 并茶几)
- Luogu 1525 【NOIP2010】关押罪犯 (贪心,并查集)