题目链接:/problemnew/show/P4578
题目描述
据古代传说记载,所罗门王既是智慧的代表,又是财富的象征。他建立了强大而富有的国家,聚集了大批的黄金象牙和钻石,并把这些价值连城的珍宝藏在一个神秘的地方,这就是世人瞩目的“所罗门王的宝藏”。多少个世纪以来,人们一直在寻找这批早已失落的古代文明宝藏,寻找盛产黄金和钻石的宝地。曾经追寻所罗门王宝藏的冒险者们都一去不回,至今没人解开这个谜题。亨利男爵在一次幸运的旅途中意外地得到了三百年前一位葡萄牙贵族留下的写在羊皮卷上的所罗门王的藏宝图和一本寻宝秘籍。在这张藏宝图的诱惑下,亨利男爵邀请约翰上校和勇敢的猎象人夸特曼开始了寻找埋葬在黑暗地底的所罗门王宝藏的艰险历程。他们横穿渺无边际的沙漠和浓荫蔽日的原始森林,越过汹涌澎湃的激流险滩,翻越高耸入云的峻岭雪山,饱尝沙漠的酷热和冰雪严寒,在藏宝图的指引下来到非洲一个原始的神秘国度库库安纳。这里有残酷的人殉制度,有一个拥有一千个妻室的独眼暴君特瓦拉,有像兀鹫一般丑恶诡诈老而不死的女巫加古尔,还有美丽聪慧的绝代佳人弗拉塔。在这片陌生而又险象环生的土地上三位寻宝英雄历尽艰辛,终于在绝代佳人弗拉塔的帮助下在海底深处找到了珍藏这批价值连城宝藏的巨大的藏宝洞。然而在女巫加古尔的精心策划下,一场灭顶之灾正在悄悄逼近。
藏宝洞的洞门十分坚固且洞门紧闭,如果不知道开启洞门的秘密是无法打开藏宝洞的洞门。在藏宝洞的洞门一侧有一个奇怪的矩形密码阵列。根据寻宝秘籍的记载,在密码阵列每行的左侧和每列的顶端都有一颗红宝石按钮。每个按钮都可以向左或向右转动。每向左转动一次按钮,相应的行或列中数字都增11。每向右转动一次按钮,相应的行或列中数字都减11。在矩形密码阵列的若干特定位置镶嵌着绿宝石。只有当所有绿宝石位置的数字与藏宝图记载的密码完全相同,紧闭的洞门就会自动缓缓打开。女巫加古尔早已得知开门的秘密。为了阻止寻宝者打开洞门,女巫加古尔为开门的密码阵列设置了全00的初始状态。试图打开洞门的寻宝者如果不能迅速转动按钮使所有绿宝石位置的数字与藏宝图记载的密码完全相同,就会自动启动藏宝洞玄妙的暗器机关,使寻宝者遭到灭顶攻击而死于非命。
您能帮助三位寻宝英雄顺利打开藏宝洞的洞门吗?
编程任务:对于给定的密码阵列,找到获得正确密码的红宝石按钮的转动序列。
输入输出格式
输入格式:
输入的第一行中有一个正整数T (T \le 5)T(T≤5)表示有TT组数据。每组数据的第一行有33个正整数n,mn,m和kk,表示洞门密码阵列共有nn行和mm列,0<n,m,k \le 10000<n,m,k≤1000。各行从上到下依次编号为1,2,\ldots,n1,2,…,n;各列从左到右依次编号为1,2,\ldots,m1,2,…,m。接下来的kk行中每行有三个整数x,y,cx,y,c,分别表示第kk个绿宝石在密码阵列中的位置和密码,xx为行号yy为列号,cc为该位置处的密码。
输出格式:
对于每组数据,用一行输出Yes
或者No
。输出Yes
表示存在获得正确密码的红宝石按钮的转动序列。输出No
则表示无法找到获得正确密码的红宝石按钮的转动序列。
输入输出样例
输入样例#1:复制
22 2 41 1 01 2 02 1 22 2 22 2 41 1 01 2 02 1 22 2 1
输出样例#1:复制
YesNo
说明
对于100%的数据,1≤n,m,k≤1000,k≤n×m,∣c∣≤1,000,000。
题意:给你一个n*m的图,刚开始全部都是0,然后问你如何操作能够满足k个位置都是它要求的数字,每次操作通过一行或者一列+1,如果最后可以变成要求的图 就输出yes否则no
① 图论法
感想:能把这样的题跟图论结合我也是服气,厉害呀~
思路:将x与(y+n) 构成双向边,如果是(x,y)的话分开记录也是可以的,但是麻烦的多。不如这样简便。
对于某个点来说他受到了来自X轴和Y轴上其他点的影响,所以对于X轴和Y轴他们的所有操作可以使得sum[x] 和 sumn[y] 都是一个值,而不是不同的值,这样的就可以,但是如果是不同的值说明对于某个点肯定是不符合题意的。
大概这样的六个点,从1开始出发,(1,2)需要+1,(1,3)需要+3,然而对于以2为开始的点,他需要翻转的次数等于边的权值减去当前已经加的数表示还需要加的数,就这样一直到最后某个点,如果是单源路肯定没问题,如果是多源路可能会碰到那种对于同一个节点需要不同的次数,这样的肯定GG 组不出来符合题意的情况,所以需要对于1-n+m每个点为开始点来判断是否满足题意。Fu~ck好题。
#include<bits/stdc++.h>using namespace std;struct node{int u;int v;int valu;int next;}no[4005];int cnt;int head[];int vis[];int sum[];void add(int u,int v,int valu){no[cnt].u=u;no[cnt].v=v;no[cnt].valu=valu;no[cnt].next=head[u];head[u]=cnt++;}int bfs(int u){queue<int>q;q.push(u);vis[u]=1;while(!q.empty()){int temp=q.front();q.pop();for(int i=head[temp];i!=-1;i=no[i].next){int tt=no[i].v;int Valu=no[i].valu;if(vis[tt]){if(sum[temp]+sum[tt]!=Valu)return 0;}else{sum[tt]=Valu-sum[temp];vis[tt]=1;q.push(tt);}}} return 1;}int main(){int t;scanf("%d",&t);while(t--){cnt=1;memset(head,-1,sizeof(head));memset(vis,0,sizeof(vis)); memset(sum,0,sizeof(sum));int n,m,k;scanf("%d%d%d",&n,&m,&k);while(k--){int x,y,w;scanf("%d%d%d",&x,&y,&w);add(x,y+n,w);add(y+n,x,w);}int flag=1;for(int i=2;i<=n+m;i++)if(vis[i]==0)flag=flag&bfs(i);if(flag)printf("Yes\n");elseprintf("No\n"); }return 0;}
②另外一种K^2的做法
K^2的做法是枚举任何2个点判断之间的关系
比如第 i 行相等,那么对于其他行的同样2列的差值应该是对应的,因为对于一行来说2个数是同时在变化的,他们之间2个数的差值取决于2列,如果2列之间的差值不相等的话,肯定会有2列不符合题意。
#include<bits/stdc++.h>using namespace std;int a[1005];int b[1005];int c[1005];int n,m,k;int vis1[1005][1005];int vis2[1005][1005];int cha1[1005][1005];int cha2[1005][1005];int check(int i,int j,int flag){if(flag==0){if(b[i] > b[j])swap(i,j);return c[i]-c[j];//小的在前边 统一}else{if(a[i] > a[j])swap(i,j);return c[i]-c[j];}}int solve(){memset(vis1,0,sizeof(vis1));memset(vis2,0,sizeof(vis2));memset(cha1,0,sizeof(cha1));memset(cha2,0,sizeof(cha2));for(int i=0;i<k;i++){for(int j=i+1;j<k;j++){if(a[i]==a[j] && b[i] == b[j] && c[i] != c[j])return 0; if(a[i]==a[j]){if(vis1[b[i]][b[j]] && cha1[b[i]][b[j]] != check(i,j,0))return 0;vis1[b[i]][b[j]]=1;vis1[b[j]][b[i]]=1;cha1[b[i]][b[j]]=check(i,j,0);cha1[b[j]][b[i]]=check(i,j,0);}else if(b[i]==b[j]){if(vis2[a[i]][a[j]] && cha2[a[i]][a[j]] != check(i,j,1))return 0;vis2[a[i]][a[j]]=1;vis2[a[j]][a[i]]=1;cha2[a[i]][a[j]]=check(i,j,1);cha2[a[j]][a[i]]=check(i,j,1);}}}return 1;}int main(){int t;scanf("%d",&t);while(t--){scanf("%d%d%d",&n,&m,&k);for(int i=0;i<k;i++)scanf("%d%d%d",&a[i],&b[i],&c[i]);if(solve())printf("Yes\n");elseprintf("No\n");}return 0;}