这道题是学长的原创题:
Move The Lab
Description
The 573 lab will move. There are some chemicals, Severus should move them. But Figo only gave him a bag to save money. So Severus can only choose some of them. If some of the chemical drug are put together, they will explode, and if a and b can explode, b and c can explode, a and c can explode too. How can Severus choose the chemicals, he can be safe and the value can be highest.Input
In the first line there are three integers v, n and m(1 ≤ n, m, k ≤ 1000).Each of the following n lines contains two integers, indicating the space occupied by the i-th chemical and the value of it.
In each of the next m lines, there are two integer a and b, if a and b are put together, they will explode.
The input file is terminated by EOF.
Output
Output one integer, the most value Severus can take.Sample Input
10 3 1
5 100
5 200
2 10
1 2
Sample Output
210
输入W背包最大容量N种化学物品 M对会爆炸的组合;
之后会给与每个化学药品占用的空间和价值;以及M对不能再一起的组合;
其实这道题的算法部分很直接,看完题就知道是并查集和01背包,但是真正关键的在于处理dp[j](dp[j]指的是在质量为j的时候最多可以装多少价值的药品)的时候判断此时背包中已经有了多少药品,基于这种思想,我写了一个二维数组来判断以下是代码:(果断T了。。。。)
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<iostream>
#define MAXN 1005
using namespace std;
int max_w,n,m;
int w[MAXN],v[MAXN];
int judge[MAXN][MAXN];
int father[MAXN];
int dp[MAXN];
int have[MAXN][MAXN];
int find_father(int x)
{
if(x==father[x]) return x;
else
return father[x]=find_father(father[x]);
}
void unite(int x,int y)
{
int xx=find_father(x);
int yy=find_father(y);
if(xx==yy) return;
else
{
father[xx]=yy;
return;
}
}
bool same(int x,int y)
{
return find_father(x)==find_father(y);
}
int DFS(int num,int wight)
{
int temp=v[num];
for(int i=1;i<=n;i++)
{
if(judge[num][i]&&have[wight][i])
{
temp-=v[i];
}
}
return temp;
}
void change(int num,int wight1,int wight2)
{
for(int i=1;i<=n;i++)
{
if(have[wight2][i])
{
if(judge[num][i])
{
have[wight1][i]=0;
}
else
{
have[wight1][i]=1;
}
}
}
have[wight1][num]=1;
}
int main()
{
int x,y;
while(scanf("%d%d%d",&max_w,&n,&m)!=EOF)
{
memset(have,0,sizeof(have));
memset(judge,0,sizeof(judge));
for(int i=1;i<=n;i++)
{
scanf("%d%d",&w[i],&v[i]);
father[i]=i;
}
for(int i=0;i<m;i++)
{
scanf("%d%d",&x,&y);
unite(x,y);
}
for(int i=1;i<=n;i++)
{
for(int j=i;j<=n;j++)
{
if(same(i,j)) judge[i][j]=judge[j][i]=1;
}
}
for(int i=1;i<=n;i++)
{
for(int j=max_w;j>=w[i];j--)
{
int temp=DFS(i,j-w[i]);
if(temp>0&&(dp[j-w[i]]+temp)>dp[j])
{
dp[j]=dp[j-w[i]]+temp;
change(i,j,j-w[i]);
}
}
}
printf("%d\n",dp[max_w]);
}
return 0;
}
经过将近两个小时的痛苦挣扎,终于想到了正确的姿势:
经过并查集处理之后,吧每个集合中的元素单独记录下来,在DP的时候每一个集合中的元素最多拿一种就可以了;以下是AC代码:
/*Source Code
Problem: 1006
Run Time: 69MS Memory: 4744K
Language:C++ JudgeStatus: Accepted*/
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<iostream>
#define MAXN 1005
using namespace std;
int max_w,n,m;
int w[MAXN],v[MAXN];
int num[MAXN][MAXN];
int father[MAXN];
int dp[MAXN];
int have[MAXN];
int find_father(int x)//并查集
{
if(x==father[x]) return x;
else
return father[x]=find_father(father[x]);
}
void unite(int x,int y)
{
int xx=find_father(x);
int yy=find_father(y);
if(xx==yy) return;
else
{
father[xx]=yy;
return;
}
}
bool same(int x,int y)
{
return find_father(x)==find_father(y);
}
int Max(int a,int b)
{
if(a>b) return a;
else
return b;
}
int main()
{
int max_ans;
int x,y;
while(scanf("%d%d%d",&max_w,&n,&m)!=EOF)
{
max_ans=0;
memset(have,0,sizeof(have));
memset(num,0,sizeof(num));
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
scanf("%d%d",&w[i],&v[i]);
father[i]=i;
}
for(int i=0;i<m;i++)
{
scanf("%d%d",&x,&y);
unite(x,y);
}
for(int i=1;i<=n;i++)//以根节点的编号为集合的编号,把每个集合中的元素拿出来
{
int temp=find_father(i);
num[temp][++have[temp]]=i;//其中have数组代表每一个集合的数量,num数组记录集合中的元素
}
for(int i=1;i<=n;i++)//DP部分
{
if(!have[i]) continue;
for(int j=max_w;j>=0;j--)
{
for(int k=1;k<=have[i];k++)//这一个循环用于枚举集合中的每一个元素,之所以把它放在最里面就是应为每一个集合之中最多选取一个
{
if(j<w[num[i][k]]) continue;
dp[j]=Max(dp[j],dp[j-w[num[i][k]]]+v[num[i][k]]);
}
}
}
printf("%d\n",dp[max_w]);//输出答案;
}
return 0;
}