标签:nic load min tps sla image 范围 printf lan
COCI 2016/2017 CONTEST #3 - Kronican
有\(n\)个无限体积的杯子,里面都有一些水,Mislav想喝掉所有的水,但他只想喝最多\(k\)杯水
所以他需要将这\(n\)杯水进行合并,将第\(i\)杯水倒进第\(j\)杯所需要的花费为\(C_{i,j}\)
问喝到所有水的最小花费
\(1\leq k\leq n\leq 20\)
\(0\leq C_{i,j}\leq 10^5\)
由于数据范围只有\(20\),首先可以枚举出最后剩下的\(k\)杯水是哪些,最多的情况数为\(C_{20}^{10}\)
对于枚举出来的每一个状态,确定了最后“剩下的水杯”是哪些。
将水杯看作一张有向图中的节点,就表示需要找出一张最小树形图(森林),使得每个“空水杯”顺着箭头走最终都会指向那些“剩下的水杯”。
又贪心可得,“剩下的水杯”严格为\(k\)杯时答案最优,所以这可能是一张包含\(k\)棵有向树的森林,故需要建立一个虚根,让所有“剩下的水杯”指向这个虚根,再对虚根做一遍最小树形图即可。
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
struct Edge{
int u,v,dis;
Edge(){}
Edge(int u,int v,int dis):u(u),v(v),dis(dis){}
};
struct Directed_MT{
int n,m;
Edge edges[400];
int vis[25],pre[25],id[25],in[25];
void init(int n){
this->n=n;
m=0;
}
void addedge(int u,int v,int dis){
edges[m++]=Edge(u,v,dis);
}
int DirMt(int root){
int ans=0;
while(1){
for(int i=0;i<n;i++)in[i]=INF;
for(int i=0;i<m;i++){
int u=edges[i].u,v=edges[i].v;
if(edges[i].dis<in[v]&&u!=v){
in[v]=edges[i].dis;
pre[v]=u;
}
}
for(int i=0;i<n;i++){
if(i==root)continue;
if(in[i]==INF)return -1;
}
int cnt=0;
memset(id,-1,sizeof(id));
memset(vis,-1,sizeof(vis));
in[root]=0;
for(int i=0;i<n;i++){
ans+=in[i];
int v=i;
while(vis[v]!=i&&id[v]==-1&&v!=root){
vis[v]=i;
v=pre[v];
}
if(v!=root&&id[v]==-1){
for(int u=pre[v];u!=v;u=pre[u])
id[u]=cnt;
id[v]=cnt++;
}
}
if(cnt==0)break;
for(int i=0;i<n;i++)
if(id[i]==-1)id[i]=cnt++;
for(int i=0;i<m;i++){
int v=edges[i].v;
edges[i].v=id[edges[i].v];
edges[i].u=id[edges[i].u];
if(edges[i].u!=edges[i].v)
edges[i].dis-=in[v];
}
n=cnt;
root=id[root];
}
return ans;
}
}MT;
int n,k,a[30];
int cost[30][30];
int solve()
{
MT.init(n+1);
for(int i=1;i<=n;i++)
{
if(a[i]==1)
MT.addedge(n,i-1,0); //注意编号从0开始,故全部-1
}
for(int i=1;i<=n;i++)
if(a[i]==0)
{
for(int j=1;j<=n;j++)
if(i!=j)
MT.addedge(j-1,i-1,cost[i][j]); //建立反向边
}
return MT.DirMt(n);
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&cost[i][j]);
for(int i=1;i<=n-k;i++)
a[i]=0;
for(int i=n-k+1;i<=n;i++)
a[i]=1;
int ans=INF;
do{
ans=min(ans,solve());
}while(next_permutation(a+1,a+1+n));
if(ans==INF)
ans=0;
printf("%d\n",ans);
return 0;
}
在学了在学了马上好……
ZJNU 2471 - Kronican (最小树形图/状压DP)
标签:nic load min tps sla image 范围 printf lan
原文地址:https://www.cnblogs.com/stelayuri/p/13774094.html