题目链接 : https://ac.nowcoder.com/acm/contest/206/A
这个题去年有幸去秦皇岛参加集训,见过这道题,当时特别菜还不会网络流,现在学了一点发现这个网络流还是比较简单的。
首先题意要求价值根据蜡烛数量有变化,因为数据不大,我们可以每个点多联几条变,写成第一区域连接汇点
区域到汇点的流量为1,费用为1,3,5,7.。。。。,因为从小到大加和,和正好为x的平方,所以的边流量都为1,因为只可以走一次,最后从原点到汇点跑个网络流就可以了
AC代码 :
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<algorithm> #include<iostream> #include<math.h> #include<vector> #include<queue> using namespace std; #define INT_MAX 0x73f3f3f typedef struct w_w{ int eend; int weight; int liu; int next; }miao; miao x[200010]; int head[200]; int cnt; int money[200]; int bian[200]; int dian[200]; int vis[200]; queue<int> q1; int spfa(int s,int e){ memset(bian,-1,sizeof(bian)); memset(dian,-1,sizeof(dian)); for(int i=0;i<=199;i++){ money[i]=INT_MAX; } money[e]=INT_MAX; while(q1.size()) q1.pop(); q1.push(s); money[s]=0; while(q1.size()){ int dang=q1.front(); //printf("%d ",dang); q1.pop(); vis[dang]=0; for(int i=head[dang];i!=-1;i=x[i].next){ int to=x[i].eend; int w=x[i].weight; if(x[i].liu>0&&money[dang]+w<money[to]){ money[to]=money[dang]+w; if(vis[to]==0) q1.push(to); vis[to]=1; bian[to]=i; dian[to]=dang; //printf("%d %d %d ",dang,i,to); //system("pause"); } } } if(money[e]!=INT_MAX) return 1; else return 0; } void add(int s,int e,int l,int w){ x[cnt].eend=e; x[cnt].weight=w; x[cnt].liu=l; x[cnt].next=head[s]; head[s]=cnt++; } int main() { int m,n; scanf("%d %d",&m,&n); memset(head,-1,sizeof(head)); cnt=0; int start=0; int eend=199; for(int i=1;i<=m;i++){ int a,b; scanf("%d %d",&a,&b); add(0,n+i,1,0); add(n+i,0,0,0); add(n+i,a,1,0); add(a,n+i,0,0); add(n+i,b,1,0); add(b,n+i,0,0); } for(int i=1;i<=n;i++){ for(int j=0;j<n+10;j++){ add(i,eend,1,2*j+1); add(eend,i,0,0-(2*j+1)); } } int sum=0; while(spfa(start,eend)){ //printf("+++ "); int minn=INT_MAX; for(int i=eend;i!=start;i=dian[i]){ int k=bian[i]; //printf("+++%d ",i); minn=min(minn,x[k].liu); } sum+=minn*money[eend]; for(int i=eend;i!=start;i=dian[i]){ int k=bian[i]; x[k].liu-=minn; x[k^1].liu+=minn; //printf("%d ",k); } } printf("%d ",sum); return 0; }