题意:
让你输出长度为n的某个序列,然后给你m个变量。
每次给某个数赋值的代价是
假设赋值a=7那么代价是3,因为7的二进制位中有3个1。
要求最后总代价最小。
输出总共要进行操作的次数,和最小代价。
先吐槽下,早期的cf题很多没有官方题解,只找到两篇中文题解...第一篇完全没搞懂大神再说什么。建图的方法是在第二篇中受到启发【虽然也没搞懂大神在说什么】
首先说下这题对我的启示:
1.现在对状态这个词越发有深的理解了,很多题目只要理解了某个抽象意义上的状态,加上他们之间的转换的方式【也有很多人说是序的因素】就基本上可以解决了。
2.费用流的计算方式是一整条路径上权值的总和。一开始建了个傻逼的图,感觉弱爆了。
思路:
这题的状态是这样的,对于每一个需要输出的数字来说,它要输出无非有两种来源,一种是选择之前没有赋值过的变量,另外一种是选择之前已经赋值的变量。问题在于当当前要输出的数字选择的变量是之前已经赋值的变量的时候,花费有两种情况,一种是之前已经赋值过相同的值了,另外一种是赋值不同,这两种情况下的花费是不一样的。对于之前已经打印过的数字来说,它的变量只能向以后的某一次操作转变,【因为如果以后不同的数字使用这个变量的话本身就没有意义了,假如下次使用这个变量是一个相同的数字的话我们认为是后来刚打印的数字的状态这种情况下跟原来是等价的】。
一开始可能存在没有被使用的变量这个时候我们可以贪心,当前有变量没有被使用那么我们使用新的变量就是了,假如之前没有相同的数字。
除了源汇外一共两组点,左边是变量和每个数字,右边是数字。【关键是将数字分成两部分了】然后建图就可以根据上边解释的干了。
#include<bits/stdc++.h> #define MAXN 800 #define MAXM 80002 #define INF 0x3f3f3f3f using namespace std; int cost[500],jilu[500],aans[500]; int zong; bool vis[MAXN]; struct edge{ edge *next,*op; int t,c,v; }ES[MAXM],*V[MAXN]; int N,M,S,T,EC=-1; int demond[MAXN],sp[MAXN],prev[MAXN]; edge *path[MAXN]; void addedge(int a,int b,int v,int c=INF){ //printf("%d %d %d %d ",a,b,v,c); edge e1={V[a],0,b,c,v},e2={V[b],0,a,0,-v}; ES[++EC]=e1;V[a]=&ES[EC]; ES[++EC]=e2;V[b]=&ES[EC]; V[a]->op=V[b];V[b]->op=V[a]; } bool SPFA(){ int u,v; for(u=S;u<=T;u++){ sp[u]=INF; } queue<int>q; prev[S]=-1; q.push(S); sp[S]=0; vis[S]=1; while(!q.empty()){ u=q.front(); vis[u]=0; q.pop(); for(edge *k=V[u];k;k=k->next){ v=k->t; if(k->c>0&&sp[u]+k->v<sp[v]){ sp[v]=sp[u]+k->v; prev[v]=u; path[v]=k; if(vis[v]==0){ vis[v]=1; q.push(v); } } } } return sp[T]!=INF; } int argument(){ int i,cost=INF,flow=0; edge *e; for(i=T;prev[i]!=-1;i=prev[i]){ e=path[i]; if(e->c<cost)cost=e->c; } for(int i=T;prev[i]!=-1;i=prev[i]){ e=path[i]; e->c-=cost;e->op->c+=cost; flow+=e->v*cost; } return flow; } int maxcostflow(){ int Flow=0; while(SPFA()){ Flow+=argument(); } return Flow; } int n,m; void init(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d",&jilu[i]); for(int j=0;j<31;j++){ if((jilu[i]>>j)&1)cost[i]++; } } S=0;T=n+n+m+1; vector<int>bef; for(int i=1,j=1;i<=n&&j<=m;i++){ bool ok=1; for(int k=0;k<bef.size();k++){ if(bef[k]==jilu[i]){ ok=0;break; } } if(ok){ bef.push_back(jilu[i]); addedge(j+n,i,cost[i],1); j++; } } for(int i=1;i<=n;i++){ addedge(i,T,0,1); addedge(S,i+n+m,0,1); } for(int i=n+1;i<=n+m;i++){ addedge(S,i,0,1); } for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ if(jilu[i]==jilu[j])addedge(i+n+m,j,0,1); else addedge(i+n+m,j,cost[j],1); } } zong=n; } int main() { init(); int ans=maxcostflow(); for(int i=1;i<=n;i++){ for(edge *k=V[i];k;k=k->next){ if(k->t>n){ if(k->v!=0&&k->c!=0){ zong++; } } } } printf("%d %d ",zong,ans); for(int i=1;i<=n;i++){ for(edge *k=V[i];k;k=k->next){ if(k->c!=0){ bool ok=1; if(k->v==0)ok=0; if(k->t<=n+m){ printf("%c=%d ",'a'-1+k->t-n,jilu[i]); printf("print(%c) ",'a'-1+k->t-n); aans[k->t-n]=jilu[i]; } else{ for(int j=1;j<=m;j++){ if(aans[j]==jilu[k->t-n-m]){ //puts("======"); if(ok)printf("%c=%d ",'a'-1+j,jilu[i]); printf("print(%c) ",'a'-1+j); aans[j]=jilu[i]; break; } } } break; } } } }