Being a Hero
Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1211 Accepted Submission(s): 381
Special Judge
But don't get too excited. The cities you take should NOT be reachable from the capital -- the king does not want to accidentally enter your area. In order to satisfy this condition, you have to destroy some roads. What's worse, you have to pay for that -- each road is associated with some positive cost. That is, your final income is the total value of the cities you take, minus the total cost of destroyed roads.
Note that each road is a unidirectional, i.e only one direction is available. Some cities are reserved for the king, so you cannot take any of them even if they're unreachable from the capital. The capital city is always the city number 1.
2 4 4 2 1 2 2 1 3 3 3 2 4 2 4 1 2 3 4 4 4 4 2 1 2 2 1 3 3 3 2 1 2 4 1 2 3 4 4
Case 1: 3 1 4 Case 2: 4 2 1 3
题意:n个点m条边的有向图,每条边有破坏话花费,现在国王在城市1,要分配给英雄一些城市,分配的原则是:只能在规定的f个城市中选若干个,这f个城市每个都有一个获利,被选择的城市要与国王所在的城市1隔离,所以选定后要花费一些费用来破坏边。问最后获利的最大值是多少,并且输出要破坏的边的序号。
思路:这个题拿到手之后很久没有思路,因为图上既有获利又有花费,不知道怎么建图,无奈只好求助网上神牛。添加汇点T,原图上的单向边依次建边,容量为花费,允许选择的f个点向汇点T连边,容量为点上权值。跑一遍最小割得到花费值cost,然后用总的能获得利润(就是f个点的权值之和)减去cost就是答案。那么怎样确定哪条边是割边呢?从源点S在残留网络中dfs遍历能走到的点,那么这些点就是属于S集,其他剩下的点就属于T集了,然后判断边的两个点所属的集合,如果属于不同的集合那么这条边就是割边。这样建边就完全转换成费用了,对于原图上的边如果被割到,那么这条边就是要破坏的,对于和汇点相连的边如果被割到,那么这个城市就是不能选的,最后最小割就是最小费用,感觉这样很巧妙。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <map>
#include <stack>
#include <vector>
#include <set>
#include <queue>
#pragma comment (linker,"/STACK:102400000,102400000")
#define pi acos(-1.0)
#define eps 1e-6
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define FRE(i,a,b) for(i = a; i <= b; i++)
#define FREE(i,a,b) for(i = a; i >= b; i--)
#define FRL(i,a,b) for(i = a; i < b; i++)
#define FRLL(i,a,b) for(i = a; i > b; i--)
#define mem(t, v) memset ((t) , v, sizeof(t))
#define sf(n) scanf("%d", &n)
#define sff(a,b) scanf("%d %d", &a, &b)
#define sfff(a,b,c) scanf("%d %d %d", &a, &b, &c)
#define pf printf
#define DBG pf("Hi\n")
typedef long long ll;
using namespace std;
#define INF 0x3f3f3f3f
#define mod 1000000009
const int maxn = 1005;
const int MAXN = 2005;
const int MAXM = 200010;
const int N = 1005;
int n,m,f;
struct Edge
{
int to,next,cap,flow;
}edge[MAXM];
int tol;
int head[MAXN];
int gap[MAXN],dep[MAXN],pre[MAXN],cur[MAXN];
void init()
{
tol=0;
memset(head,-1,sizeof(head));
}
//加边,单向图三个参数,双向图四个参数
void addedge(int u,int v,int w,int rw=0)
{
edge[tol].to=v; edge[tol].cap=w; edge[tol].next=head[u];
edge[tol].flow=0; head[u]=tol++;
edge[tol].to=u; edge[tol].cap=rw; edge[tol].next=head[v];
edge[tol].flow=0; head[v]=tol++;
}
//输入参数:起点,终点,点的总数
//点的编号没有影响,只要输入点的总数
int sap(int start,int end,int N)
{
memset(gap,0,sizeof(gap));
memset(dep,0,sizeof(dep));
memcpy(cur,head,sizeof(head));
int u=start;
pre[u]=-1;
gap[0]=N;
int ans=0;
while (dep[start]<N)
{
if (u==end)
{
int Min=INF;
for (int i=pre[u];i!=-1;i=pre[edge[i^1].to])
if (Min>edge[i].cap-edge[i].flow)
Min=edge[i].cap-edge[i].flow;
for (int i=pre[u];i!=-1;i=pre[edge[i^1].to])
{
edge[i].flow+=Min;
edge[i^1].flow-=Min;
}
u=start;
ans+=Min;
continue;
}
bool flag=false;
int v;
for (int i=cur[u];i!=-1;i=edge[i].next)
{
v=edge[i].to;
if (edge[i].cap-edge[i].flow && dep[v]+1==dep[u])
{
flag=true;
cur[u]=pre[v]=i;
break;
}
}
if (flag)
{
u=v;
continue;
}
int Min=N;
for (int i=head[u];i!=-1;i=edge[i].next)
if (edge[i].cap-edge[i].flow && dep[edge[i].to]<Min)
{
Min=dep[edge[i].to];
cur[u]=i;
}
gap[dep[u]]--;
if (!gap[dep[u]]) return ans;
dep[u]=Min+1;
gap[dep[u]]++;
if (u!=start) u=edge[pre[u]^1].to;
}
return ans;
}
bool vis[MAXN];
int out[MAXN];
void dfs(int u)
{
if (vis[u]) return ;
vis[u]=true;
for (int i=head[u];~i;i=edge[i].next)
{
int v=edge[i].to;
if (edge[i].cap-edge[i].flow>0)
dfs(v);
}
return ;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("C:/Users/lyf/Desktop/IN.txt","r",stdin);
#endif
int i,j,t,u,v,w,cas=0;
sf(t);
while (t--)
{
init();
sfff(n,m,f);
for (i=0;i<m;i++)
{
sfff(u,v,w);
addedge(u,v,w);
}
int T=0,all=0;
for (i=0;i<f;i++)
{
sff(u,w);
addedge(u,T,w);
all+=w;
}
printf("Case %d: %d\n",++cas,all-sap(1,T,n+1));
mem(vis,false);
dfs(1);
int cnt=0;
for (i=0;i<2*m;i+=2)
{
if (vis[edge[i^1].to]&&!vis[edge[i].to])
out[cnt++]=i/2;
}
printf("%d",cnt);
for (i=0;i<cnt;i++)
pf(" %d",out[i]+1);
pf("\n");
}
return 0;
}