您的位置:首页 > 其它

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中定义变量)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: