Description
\(n\)个节点,每个节点有一个攻击代价和需要攻击的次数。
有\(k\)个关系,攻击\(x\)后,\(y\)的攻击代价变成\(z\)。
Solution
朱刘算法。
这个好像就是求什么最小树形图的东东...
最小树形图跟最小生成树差不多,不过最小生成树是无向图,最小树形图是有向图,让一个节点和其他节点联通的最小代价。
这个建模也非常容易,只需要确定第一次的攻击即可,之后的一定都以最小代价攻击。
朱刘算法也很简单,步骤就是
找到每个节点的最小入边。
统计答案。
找环,缩环,把边权设成原边权-最小入边边权。
Code
/**************************************************************
Problem: 2260
User: BeiYu
Language: C++
Result: Accepted
Time:8 ms
Memory:1340 kb
****************************************************************/
#include <bits/stdc++.h>
using namespace std;
inline int in(int x=0,char ch=getchar()) { while(ch>'9' || ch<'0') ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();return x; }
const int N = 55;
const int M = N*N+50;
struct Edge { int fr,to;double v; };
Edge edge[M];
int n,m,rt;
int b[N];
double mic[N];
double miv[N];
int pre[N],vis[N],id[N];
void AddEdge(int fr,int to,double v) { edge[++m]=(Edge){ fr,to,v }; }
double Solve(int n) {
double res=0;
for(int cnt,tmp;;) {
for(int i=1;i<=n;i++) miv[i]=1e9,pre[i]=0;
for(int i=1;i<=m;i++) {
Edge &e=edge[i];
if(e.v<miv[e.to]) miv[e.to]=e.v,pre[e.to]=e.fr;
}
for(int i=1;i<=n;i++) if(pre[i]) res+=miv[i];
memset(vis,0,sizeof(vis));
memset(id,0,sizeof(id));
vis[0]=tmp=1,cnt=0;
for(int i=1;i<=n;i++) if(!vis[i]) {
++tmp;int u=i;
for(;!vis[u];u=pre[u]) vis[u]=tmp;
if(vis[u]==tmp) {
++cnt;
for(;!id[u];u=pre[u]) id[u]=cnt;
}
}
if(!cnt) break;
for(int i=1;i<=n;i++) if(!id[i]) id[i]=++cnt;
int mm=m;m=0;
for(int i=1;i<=mm;i++) {
Edge &e=edge[i];
if(id[e.fr]!=id[e.to]) AddEdge(id[e.fr],id[e.to],e.v-miv[e.to]);
}
n=cnt;
}return res;
}
int main() {
n=in(),rt=n+1;
for(int i=1;i<=n;i++) {
double x;scanf("%lf",&x);
mic[i]=x;
b[i]=in();
if(b[i]) AddEdge(rt,i,x);
}
for(int k=in();k--;) {
int x=in(),y=in();
double z;scanf("%lf",&z);
if(b[x] && b[y]) {
AddEdge(x,y,z);
mic[y]=min(mic[y],z);
}
}
double ans=Solve(n+1);
// cout<<ans<<endl;
// for(int i=1;i<=n;i++) cout<<mic[i]<<" ";cout<<endl;
for(int i=1;i<=n;i++) if(b[i]) ans+=(b[i]-1)*mic[i];
printf("%.2lf\n",ans);
return 0;
}