http://acm.hdu.edu.cn/showproblem.php?pid=5545
题意:
有T(30)组数据。
对于每组数据有n(1e5)个村庄,m(1e5)个战场
对于村庄i,曹操可以选择支付c[i]*num元,(0<=c[i]<=1e5)
使得这个村庄派出num个士兵,在战场x[i]为曹操作战,在战场y[i]为袁绍作战。(1<=x[i],y[i]<=m)
然后,我们有一个关键性的问题就是,如何保证题目的要求呢?
首先我们简化问题,只去考虑合法性。
对于所有战略价值为2的战场,如果从这个样的点ST出发,沿着图,能够达到任意一个战略价值为0的点。
那说明,这个战略价值为2的战场,可以使得其上的曹兵比袁兵多。
为什么能说明这个呢?
很显然,我们对于一条边,使得ST的曹兵+1,下一个点的袁兵+1。
但是这样,对于下一个点是有影响的。
如果下个点重要度为0,显然这样已经可以了。
如果下个点重要度为1,我们还要继续把这1个人转移出去,一直转移到重要度为0的点上即可。
如果下个点重要度为2,我们依然一定要把这1个人转移出去。而只要转移出去了,下个点实际上其实并没有受到影响。
枚举每个重要度为2的点,
对于每个这样的点,求它到重要度为0的点中距离最近的那个的距离。
然后这些距离求和即可。
但是这样是多源最短路。
然而我们发现,只要把边逆过来,初始化所有重要度为0的点为起点,都归为ZERO,求最短路。
然后累加所有重要度为2的点的到 ZERO的最短路即可。
这道题最短路我写的有问题,因此一直WRONG!
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int inf=0x7f7f7f7f;
int t,n,m;
const int maxn=1e5+10;
struct node{
int to;ll cost;
}a[maxn];
vector<node>e[maxn];
queue<int>q;
int x[maxn],vis[maxn],y[maxn],c[maxn],w[maxn];
ll dis[maxn];
void init(){
for(int i=0;i<maxn;i++){
dis[i]=1e15;e[i].clear();vis[i]=0;
}
while(!q.empty()) q.pop();
}
ll bfs(){
while(!q.empty()){
int now=q.front();q.pop();
vis[now] = 0;//出队列就一定要消除标记,因为标记是用来判断是不是需要入队的,这个点有可能再被其他的点更新到,而此时它需要再次入队,去用新的值来更新后面的值!
int size=e[now].size();
for(int i=0;i<size;i++){
int to=e[now][i].to;
// if(vis[to]) continue;
// vis[to]=1;//这处就太残了,dis是一直都需要更新的
if(dis[to]>dis[now]+e[now][i].cost){
dis[to]=dis[now]+(ll)e[now][i].cost;
if(vis[to] == 0){//标记是判断是否需要入队的,已经入队了就不用再入队了!
q.push(to);
vis[to] = 1;
}
}
}
}ll sum=0;
for(int i=1;i<=m;i++){
if(w[i]==2){
if(dis[i]==1e15) return -1;
sum+=dis[i];
}
}
return sum;
}
int main(){
cin>>t;int count=1;
while(t--){
scanf("%d%d",&n,&m);
init();
for(int i=1;i<=n;i++) scanf("%d",&x[i]);
for(int i=1;i<=n;i++) scanf("%d",&y[i]);
for(int i=1;i<=n;i++) scanf("%d",&c[i]);
for(int i=1;i<=m;i++) {
scanf("%d",&w[i]);
}
for(int i=1;i<=n;i++){
e[y[i]].push_back({x[i],c[i]});
if(w[y[i]]==0) {q.push(y[i]);dis[y[i]]=0;vis[y[i]]=1;}
}
printf("Case #%d: ",count++);
printf("%lld\n",bfs());
}
}