最小树形图+朱刘算法
朱刘算法:http://acm.nudt.edu.cn/~twcourse/Tree.html#a17 啊虽然是繁体的不过写的挺详细的, 还有啊那个不叫水母叫环套树......
为什么网上的朱刘算法都没有正确性证明啊,好气噢……感性理解一下吧
这题的话,显然先把全部的都打一遍再把每一个打完,不会比其他方法更劣,因为其他方法通过交换打的过程都能变成前者。那就变成我们要找到最小的代价能把每一个都打一遍,显然是一个最小树形图问题,套朱刘算法即可,本题不会无解。
交代码的时候遇到恶意卡评测,一堆代码在Pending,我该说什么好。(截至写完这一篇,已经卡了一页半了,不知道之后会怎样)
看到了一些心碎的事情,学姐R.I.P.
#include<cstdio>
#include<algorithm>
#define N 55
#define K 23333
using namespace std;
namespace runzhe2000
{
typedef double db;
const double INF = 1e9;
struct pdge{int from, to; db val;}pe[K];
int pecnt, n, k, b[N], S, pre[N], vis[N], bel[N], ban[N];
db a[N], ans, in[N];
db DMST()
{
db r = 0;
for(;;)
{
for(int i = 1; i <= n; i++) in[i] = INF, pre[i] = vis[i] = bel[i] = 0;
for(int i = 1; i <= pecnt; i++)
{
int from = pe[i].from, to = pe[i].to;
if(from != to && pe[i].val < in[to])
in[to] = pe[i].val, pre[to] = from;
}
int findring = 0;
for(int i = 1, j; i <= n; i++) if(!ban[i])
{
r += in[i];
for(j = i; !vis[j] && j; j = pre[j]) vis[j] = i;
if(vis[j] == i)
{
for(int k = pre[j]; k != j; k = pre[k]) bel[k] = j;
bel[j] = j; findring = 1;
}
if(!bel[i]) bel[i] = i;
}
for(int i = 1; i <= n; i++) if(!ban[i] && bel[i] != i) ban[i] = 1;
if(!findring) return r;
for(int i = 1; i <= pecnt; i++)
{
int &from = pe[i].from, &to = pe[i].to; db &val = pe[i].val;
if(from != to)
{
val -= in[to];
from = bel[from];
to = bel[to];
}
}
}
return -233;
}
void main()
{
scanf("%d",&n); S = n + 1;
for(int i = 1; i <= n; i++)
{
scanf("%lf%d",&a[i],&b[i]);
pe[++pecnt] = (pdge){S, i, a[i]};
}
scanf("%d",&k);
for(int i = 1; i <= k; i++)
{
int x, y; db z;
scanf("%d%d%lf",&x,&y,&z);
pe[++pecnt] = (pdge){x, y, z};
a[y] = min(a[y], z);
}
ans += DMST();
for(int i = 1; i <= n; i++)
ans += (b[i] - 1) * a[i];
printf("%.2lf\n",ans);
}
}
int main()
{
runzhe2000::main();
}