A - True Liars
题意:
那么如果一个人说另一个人是好人,那么如果这个人是好人,说明 对方确实是好人,如果这个是坏人,说明这句话是假的,对方也是坏人。
如果一个人说另一个人是坏人,那么如果这个人是好人,说明对方是坏人,如果这个是坏人,说明 对方是好人。
也就是如果条件是yes说明这两个是相同集合的,否则是两个不同的集合。
思路:
用r[i]表示i结点与根结点的关系,0为相同集合,1为不同集合。这是一个经典的并查集问题。
这样处理之后,还需要判断是否唯一
我们通过并查集,可以将所有人分为若干个集合,其中对于每一个集合,又分为两个集合(好人和坏人,但是不知道哪些是好人,哪些是坏人,我们只有相对关系)
接下来就是从所有大集合中的两个小集合取一个,组成好人集合,判断是否唯一。
背包问题,dp[i][j]表示前i个大集合,好人为j个的方案有多少种,或者dp[i][j]表示当前好人i个,坏人j个的情况有多少种
如果dp[cnt][p1]!=1说明方案不唯一,或者无解。
输出方案就是加个pre数组,从后往前递推呢。
代码:
/* POJ 1417 */ #include#include #include #include <string.h> #include #include <string> using namespace std; const int MAXN=610; int F[MAXN]; int val[MAXN]; int find(int x) { if(F[x]==-1)return x; int tmp=find(F[x]); val[x]+=val[F[x]]; val[x]%=2; return F[x]=tmp; } int a[MAXN][2];//a[i][0],a[i][1]表示每个大集合分成两部分的个数 vector<int>b[MAXN][2]; bool used[MAXN]; int dp[MAXN][MAXN/2]; int pre[MAXN][MAXN/2]; int main() { int n,p1,p2; while(scanf("%d%d%d",&n,&p1,&p2)==3) { if(n==0 && p1==0 && p2==0)break; memset(F,-1,sizeof(F)); memset(val,0,sizeof(val)); int u,v; char str[10]; while(n--) { scanf("%d%d%s",&u,&v,&str); int tmp; if(str[0]=='y')//相同 tmp=0; else tmp=1;//相反 int t1=find(u),t2=find(v); if(t1!=t2) { F[t1]=t2; val[t1]=(val[v]-val[u]+tmp+2)%2; } } for(int i=0;i ) { b[i][0].clear(); b[i][1].clear(); a[i][0]=0; a[i][1]=0; } memset(used,false,sizeof(used)); int cnt=1; for(int i=1;i<=p1+p2;i++) if(!used[i]) { int tmp=find(i); for(int j=i;j<=p1+p2;j++) { if(find(j)==tmp) { used[j]=true; b[cnt][val[j]].push_back(j); a[cnt][val[j]]++; } } cnt++; } memset(dp,0,sizeof(dp)); dp[0][0]=1; for(int i=1;i ) { for(int j=p1;j>=0;j--) { if(j-a[i][0]>=0 && dp[i-1][j-a[i][0]]) { dp[i][j]+=dp[i-1][j-a[i][0]]; pre[i][j]=j-a[i][0]; } if(j-a[i][1]>=0 && dp[i-1][j-a[i][1]]) { dp[i][j]+=dp[i-1][j-a[i][1]]; pre[i][j]=j-a[i][1]; } } } if(dp[cnt-1][p1]!=1) { printf("no\n"); } else { vector<int>ans; ans.clear(); int t=p1; //printf("%d\n",cnt); for(int i=cnt-1;i>=1;i--) { int tmp=t-pre[i][t]; //printf("%d\n",i); //printf("%d %d\n",t,tmp); if(tmp==a[i][0]) { for(int j=0;j0];j++) ans.push_back(b[i][0][j]); } else { for(int j=0;j1];j++) ans.push_back(b[i][1][j]); } t=pre[i][t]; } sort(ans.begin(),ans.end()); for(int i=0;i ) printf("%d\n",ans[i]); printf("end\n"); } } return 0; }
B - The Shortest Path in Nya Graph
题意:有n个点 , m条无向边 ,每条边都是有权值, 并且每个点属于一个楼层 ,楼层上的点到可以到相邻楼层的任意一点 ,但是要花费 c 。没有点的相邻楼层不能互达。求 1 到 n的最小花费。
思路:图建好了就是裸的最短路了。但是建图有点麻烦,参考了别人的代码之后才明白为什么要这样建图。
把楼层看成一个点,第i层可以看成第n+i个点。楼层与该楼层上的点建边,边权为0,单向;楼层与
相邻楼层建边,边权为C,双向;相邻楼层上的点与该楼层建边,边权为C,单向。
代码:
#include#include #include #include #include #include #include #include #define N 200100 #define ll long long using namespace std; const int inf=0x3f3f3f3f; int n,m,c; int lay[N]; bool hava[N]; bool vis[N]; int cnt[N]; int dist[N]; struct Edge { int v; int cost; Edge(int _v=0,int _c=0):v(_v),cost(_c) {} bool operator <(const Edge &r)const { return cost>r.cost; } }; vector E[N]; void addedge(int u,int v,int w) { Edge it; it.v=v; it.cost=w; E[u].push_back(it); } void Dijk(int n,int start) { //点的编号从1开始 memset(vis,false,sizeof(vis)); for(int i=1; i<=n*2; i++)dist[i]=inf; priority_queue que; while(!que.empty())que.pop(); dist[start]=0; que.push(Edge(start,0)); Edge tmp; while(!que.empty()) { tmp=que.top(); que.pop(); int u=tmp.v; if(vis[u])continue; vis[u]=true; for(int i=0; i ) { int v=E[tmp.v][i].v; int cost=E[u][i].cost; if(!vis[v]&&dist[v]>dist[u]+cost) { dist[v]=dist[u]+cost; que.push(Edge(v,dist[v])); } } } } int main() { // freopen("test.in","r",stdin); int t; cin>>t; int ca=1; while(t--) { for(int i=0; i<=n*2+1; i++)E[i].clear(); scanf("%d%d%d",&n,&m,&c); memset(hava,0,sizeof hava); for(int i=1; i<=n; i++) { scanf("%d",&lay[i]); hava[lay[i]]=true; } int u,v,cost; for(int i=1; i<=m; i++) { scanf("%d%d%d",&u,&v,&cost); addedge(u,v,cost); addedge(v,u,cost); } if(n<=1) { printf("Case #%d: 0\n",ca++); continue; } for(int i=1; i ) { if(hava[i]&&hava[i+1]) { addedge(n+i,n+i+1,c); addedge(n+1+i,n+i,c); } } for(int i=1; i<=n; i++) { addedge(lay[i]+n,i,0); if(lay[i]>1)addedge(i,lay[i]-1+n,c); if(lay[i] 1+n,c); } Dijk(n,1); printf("Case #%d: %d\n",ca++,dist[n]>=inf?-1:dist[n]); } return 0; }
C - The Super Powers
题意:给你一个关于超级幂数的定义,只要一个数是至少两个数的幂,即为超级幂数。例如64=8*8=4*4*4。输出1~2^64-1内所有的超级幂数。
思路:一开始的思想暴力,但是肯定很慢。有两个比较重要的思路:① 找num^x<2^64-1的时候,尝试两边取对数,然后确定x 还有很重要的一点,pow会出错。谨慎使用,wa了很多次,用快速幂才ac。 扔进set里遍历输出即可。 代码: 题意:给你n组区间,每个居间都带有权值,让你选取一些区间使得区间不想交并且权值最大(要每组都至少有一个区间被选到)。 思路:n最多十组,我们就可以用二进制来记录当前唱歌的状态,然后离散化区间点(这样我们就可以用连续的点去表示这些区间),这样就有dp【i】【j】,i表示当前的区间点,j表示当前的状态,dp【i1】【j1】=dp【i】【j】+num【k】。每次寻找当前点往右 能更新的最近的一个区间(一切切的一切都是膜拜大佬的博客来的,离散化真是个好东西) 代码: 题意:在二维坐标上,有动物的栖息地和一条河流,河流在y=0上,即为X轴。现在要建一个圆形的动物保护区,让动物都在里面,同时这个圆要求和X轴相切。因为只能相切,如果出现动物在X轴两侧的情况输出-1,即不能满足情况。 思路:我们二分半径,然后由于固定了与X轴相切,我们对于每一个点,就可以算出这个点在圆上的时候的圆心的极限距离。然后我们就可以求得每一个点的圆心的区间范围。然后所有点的区间范围都相交,那么证明这个半径可行,否则不可行。 代码:如果你的电脑和我的一样辣鸡,代码基本相同样例跑不出来,可能是精度的问题,建议您不要取很大的数字,取小点吧。 题意:给出n,a,b,问,是否有这样的一个图,有n个节点,分成a块,现将两两节点之间有边的把边删除,两两节点之间无边的建边,使得形成的图分成b块。如果有,输出YES,并且输出这个图的邻接矩阵,否则输出NO。这题难就难在是否能推出结论以及答案的输出。 思路: 任何一个图,不管分成多少块,经过上述操作后,必然变成一个联通图。 证:有n个节点,分成m块,任取一块,经过上述操作后,所取的这一块内的任意节点必定与其他块的所有节点相连! 这个结论换句话说就是,a和b里面,至少有一个数是1!知道这个结论就简单了,首先排除所有不含1的情况,然后,b==1的情况,我们将图分成a块,直接另前a-1块都只有一个节点,最后1块包含剩下的节点就行了。当然,对于a==1,只要将要输出反过来一下就可以了(注意,这里要讨论一下a和b都为1的情况,当n==2和n==3时,是不行的直接排除,其他情况都是可行的,特殊处理一下a和b都等于1的输出) 代码: 1 /*
2 I have a dream!A AC deram!!
3 orz orz orz orz orz orz orz orz orz orz orz
4 orz orz orz orz orz orz orz orz orz orz orz
5 orz orz orz orz orz orz orz orz orz orz orz
6
7 */
8
9 #include
D - Music Festival
---------------------
作者:liexss
来源:CSDN
原文:https://blog.csdn.net/liexss/article/details/83511980 1 #include
E - Nature Reserve
1 /*
2 I have a dream!A AC deram!!
3 orz orz orz orz orz orz orz orz orz orz orz
4 orz orz orz orz orz orz orz orz orz orz orz
5 orz orz orz orz orz orz orz orz orz orz orz
6
7 */
8
9 #include
F - Graph And Its Complement
---------------------
作者:JIA-YAO
来源:CSDN
原文:https://blog.csdn.net/qq_41874469/article/details/80658410 #include "iostream"
#include "algorithm"
using namespace std;
int n,a,b;
char ch1='0',ch2='1';
int main() {
cin>>n>>a>>b;
if (a!=1&&b!=1||a==1&&b==1&&n<=3&&n>=2||a>n||b>n) {cout<<"NO"<