2016年中国大学生程序设计竞赛(合肥) 题解小结 1001.传递 1003.朋友 1005.扫雷

15 篇文章 0 订阅
3 篇文章 0 订阅

开始听说这场比赛还比较难,但是做了以后感觉其实并不难,只不过是自己太菜了~基本上都是用不到什么算法的题目

前面两道水题还卡了一会,还是室友找到了错误。总结了一下就是能不优化就不优化,没必要的优化就可能会错。无脑暴力就好了。


1001题  传递

先说A题,这道题如果推的话应该是很难的但是有一个比较投机的做法就是随机1e7的数据(这个很容易),然后就过了。随机的次数要多,并且还得看运气....换了个种子可能就会过不了。不过也是学到了一个做法。

代码:

#include<bits/stdc++.h>
using namespace std;

#define LL long long

char str[2020][2020];

int main() {
    int t;
    scanf("%d",&t);
    while(t--) {
        int n;
        scanf("%d",&n);
        for(int i = 0;i < n;i++)
            scanf("%s",str[i]);
        int cnt = 0,flag = 1;
        //srand(time(0)); //加上这句就过不了,orz
        while(cnt < 10000000) { //随机一千万组数据
            int a = rand()%n;
            int b = rand()%n;
            int c = rand()%n;  //%n确保数据范围在0到n
            cnt++;
            if(str[a][b] == 'Q' && str[b][c] == 'Q' && str[a][c] != 'Q') {
                flag = 0;
                break;
            }
            if(str[a][b] == 'P' && str[b][c] == 'P' && str[a][c] != 'P') {
                flag = 0;
                break;
            }
        }
        if(flag) printf("T\n");
        else printf("N\n");
    }
}


1003题  朋友

这道题算是博弈论中的一个题型,但是没学过的人也该有可以做。

要找到一个规律,如果只考虑一条路,那么根的最近的边是1,先选的人必胜,否则先选的人必败。

那么我们只要考虑和当前根结点相连接的边的权值的和,mod 1 后如果为1就是先选的人(girl)胜,否则后选的人(boy)胜。 

还有要考虑的就是更新边权,要注意如果边(a,b)本来权值就是1,题目要求(1,a,b,1),那么相当于不更新,这样的话就需要一个map来存边权的信息,再更新即可。

#include<bits/stdc++.h>
using namespace std;

#define LL long long
int va[40005];
typedef pair<int,int> pii;
map<pii,int> edge;

int main() {
	int t;
	scanf("%d",&t);
	while(t--) {
		int n,m;
		scanf("%d%d",&n,&m);
		memset(va,0,sizeof(va));
		for(int i = 1;i < n;i++) {
			int x,y,v;
			scanf("%d%d%d",&x,&y,&v);
			if(x > y) swap(x,y); //将x,y的值排序,就不用再记录edge[pii(y,x)]了
			edge[pii(x,y)] = v;
			if(v) {
				va[x]++;va[y]++;
			}
		}
		for(int i = 1;i <= m;i++) {
			int q,x,y,z;
			scanf("%d",&q); 
			if(q) {
				scanf("%d%d%d",&x,&y,&z);    //更新边权的信息
				if(x > y) swap(x,y);
				if(z && !edge[pii(x,y)]) {
					va[x]++;va[y]++;
					edge[pii(x,y)] = 1;
				}
				else if(!z && edge[pii(x,y)]) {
					va[x]--;va[y]--;
					edge[pii(x,y)] = 0;
				}
			}
			else {
				scanf("%d",&x);
				if(va[x] % 2 == 0) printf("Boys win!\n");
				else printf("Girls win!\n");
			}
		}
	}
}



1005题  扫雷

这题就是一个简单的递推或者dp,千万不能把它想的太难,就是总结规律,找到联系。

因为有三行N列,中间的已经知道是数字,上下两行放地雷。那么对于每一列,我们只要考虑四种情况。

1、第一行和第三都不放  总数是0个

2、第一放,第不放  总数是1个

3、第一不放,第放  总数是1个

4、第一和第都放     总数是2个

这样的话,对于每一列,就是放0个地雷和2个地雷都是一种情况,放1个的时候有两种。

我们就可以先对第一列的所有情况遍历,然后后面怎么放就可以由第一列的情况推出来,不符合条件的就跳过,最后得出结果。

具体看代码:

#include<bits/stdc++.h>
using namespace std;

const long long mod = 1e8+7;
char str[10005];
int num[10005],a[10005];

int main() {
	//freopen("out.txt","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--) {
        scanf("%s",str);
        int n = strlen(str);
        for(int i = 0;i < n;i++) 
            num[i] = str[i] - '0';
        int dd,flag;
        long long ans = 0;
        if(n == 1) {  //单独考虑长度为1的时候
            if(num[0] == 0 || num[0] == 2) printf("1\n");
            else if(num[0] == 1) printf("2\n");
            else printf("0\n");
            continue;
        }
        if(num[0] > 4) {printf("0\n"); continue;}
        for(int i = 0;i <= num[0] && i <= 2;i++) {  //遍历第一列由多少情况
            a[1] = num[0] - i;
            a[0] = i;flag = 1;
            for(int j = 1;j <= n-1;j++) {
                a[j+1] = num[j] - (a[j-1]+a[j]);
                if(a[j+1] < 0 || a[j+1] > num[j+1] || a[j+1] > 2) {
                    flag = 0;
                    break;
                }
            }
	    if(a[1] > 2 || a[1] < 0) continue;
            if(num[n-1] != a[n-1]+a[n-2]) flag = 0;
            if(!flag) {
                continue;
            }
            dd = 1;
            for(int i = 0;i < n;i++) {
		if(a[i]+a[i-1]+a[i+1] != num[i]) continue;
                if(a[i] == 1)   //放1的时候方案数*2并且取余
                    dd = (dd*2)%mod;
            }
            ans = (dd+ans) % mod;
        }
        printf("%I64d\n",ans % mod);
    }
}

总结了还是要多练,还有就是脑洞要打开,不要把题目想的太难了,总结了规律,铜牌的题还是很好做的。

继续加油吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值