题意
现有一款游戏,你作为玩家,拥有k种物品。开始时,每种物品有1000件。
现在,在你面前有n个任务,每种任务都可能消耗一些物品,也可能得到一些物品。做第i个任务的物品得失情况用一个包含k个字母的字符串Si表示,其中每个字母都是+,-,/中的一种,第j个字母表示该任务对物品j的数量的影响。+表示做这个任务能得到一个物品j,-表示做这个任务会消耗一个物品j,/表示做这个任务对物品j的数量不产生影响。
但是,做任务是有前提条件的。游戏设计者约定了m个限制关系,每个限制关系是一个有序数对(i, j),表示做任务i前必须先做任务j。
现在,你需要选择性的做一些任务,使得获得的物品最多。
比较两种方案的获得的物品数的方法如下:先比较他们获得第物品1的个数,若相同,再比较物品2,若相同,再比较物品3,以此类推。
数据范围
n ≤ 1000 , m ≤ 5000 , k ≤ 5 , 保 证 限 制 关 系 中 没 有 环 n\le 1000 ,m\le 5000 ,k\le 5,保证限制关系中没有环 n≤1000,m≤5000,k≤5,保证限制关系中没有环
解法
考虑一个任务的奖励很像k位数,所以将其转化成10进制数,然后权值有正有负,考虑对于限制关系建图,方式就是如果做任务i之前需要做任务j,那就从i向j连一条边,然后对于这个图求最大权闭合子图.选出的子图就是答案.
由于我一开始对这样做的正确性抱有怀疑,所以现在稍微证明一下:
首先最大权闭合子图是用所有正权值之和减去原图的最小割,然后我们相对于限制反向建边,就是要求如果要选一个点,则它之前的所有点必须要选.这样就满足了限制.
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf=1e18;
const int maxn=5e3+5;
inline int read(){
char c=getchar();int t=0,f=1;
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
return t*f;
}
int n,m,k;
ll p[maxn];
char c[maxn][5];
ll val[maxn];
inline int calc(char c){
if(c=='+')return 1;
if(c=='-')return -1;
return 0;
}
struct edge{
int v,p;
ll w;
}e[maxn<<1];
int h[maxn],cnt=1;
inline void add(int a,int b,ll c){
e[++cnt].p=h[a];
e[cnt].v=b;
e[cnt].w=c;
h[a]=cnt;
}
int s,t,ht[maxn];
ll dis[maxn];
bool bfs(){
queue<int> q;
while(!q.empty())q.pop();
q.push(s);
memset(dis,0,sizeof(dis));
dis[s]=1;
while(!q.empty()){
int u=q.front();q.pop();
for(int i=h[u];i;i=e[i].p){
int v=e[i].v;
if(e[i].w&&dis[v]==0){
dis[v]=dis[u]+1;q.push(v);
}
}
}
return dis[t];
}
ll dfs(int u,ll rest){
if(rest==0||u==t){return rest;}
ll tot=0;
for(int &i=ht[u];i;i=e[i].p){
int v=e[i].v;
if(e[i].w&&dis[v]==dis[u]+1){
ll di=dfs(v,min(rest,e[i].w));
e[i].w-=di;e[i^1].w+=di;
rest-=di;tot+=di;
if(rest==0)break;
}
}
return tot;
}
ll dinic(){
ll ans=0;
while(bfs()){
ll di=0;
for(int i=1;i<=t;i++)ht[i]=h[i];
while(di=dfs(s,inf))ans+=di;
}
return ans;
}
ll tmp1[5],tmp2[5];
int main(){
//freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
n=read(),m=read(),k=read();s=n+1,t=n+2;
p[0]=1;
for(int i=1;i<k;i++)p[i]=p[i-1]*2*n;
for(int i=1;i<=n;i++){
scanf("%s",c[i]);
for(int j=0;j<k;j++){
val[i]+=p[k-1-j]*calc(c[i][j]);
}
}
for(int i=1;i<=m;i++){
int a=read(),b=read();
add(a,b,inf);
add(b,a,0);
}
ll sum=0;
for(int i=1;i<=n;i++){
if(val[i]>0){
sum+=val[i];
add(s,i,val[i]);add(i,s,0);
}
else{
add(i,t,-val[i]);add(t,i,0);
}
}
ll ans=dinic();
if(sum<ans){
for(int i=0;i<k;i++)printf("1000 ");
return 0;
}
else{
sum-=ans;
for(int i=0;i<k;i++){
if(p[1]){
tmp1[k-1-i]+=sum%p[1];
sum/=p[1];
}
else{
tmp1[k-1-i]+=sum;
}
//printf("%d\n",tmp1[k-1-i]);
if(tmp1[k-1-i]>n){
tmp1[k-i-2]++;tmp1[k-1-i]=tmp1[k-1-i]-2*n;
//printf("%d\n",k-i);
}
}
for(int i=0;i<k;i++){
printf("%lld ",1000+tmp1[i]);
}
return 0;
}
return 0;
}