Vijos P1324 最优组播树(这再一次证明了状态函数设置恰当的重要性)

来源:互联网 发布:2017网络神曲 编辑:程序博客网 时间:2024/06/04 19:15
【问题描述】
 
  有 n 个通信员,分别编号为p[0],p[1],…,p[n-1]。消息从p[0]处开始传播,然后经过相互转发,最后当所有通信员收到消息以后,发布工作完成。具体过程是这样的:
  最开始只有p[0]知道消息,他可以从那些不知道消息的人中任意选择一个人,设为p[i],并将消息告知p[i]。接下来p[0]与p[i]再从剩下的人中再分别选择一个人,将消息告知,这样,直到所有的人都知道消息,则消息发布完成。任意两个人之间的消息传递都要耗费一定的时间,这个时间不一定是相同的。对于每个通信员,都有一个工作量的上限,即他最多只能将消息告知的人数。
  而且每个人都有一个传递深度限制。所谓传递深度限制,即为消息从p[0]到该人所转发的次数。如果消息从p[0]传递给p[1],则p[1]的传递深度为1,再从p[1]传递给p[2],则p[2]的传递深度为2.所有通信员的传递深度都不能大于D.
  现在,给出通信员的人数n,任意两个通信员传递信息所需要的时间,每个通信员的工作量上限,所有通信员的传递深度限制。求一个消息从p[0]传递到所有人最少需要多少时间? 
 
【输入格式】
 
  输入文件包含多组数据
  每组数据第一行是整数n(1<=n<=15),表示有n个通信员,消息最初从P0开始发布。
  接下来n行:每行n个整数。标号从0开始到n-1,第i行j列的值Tij是通信员i把消息告知j所需的时间。Tij=Tji恒成立。
  第n+3行:n个整数C[0],……,C[n-1],表示P[0]至P[n-1]的工作量上限。
  第n+4行:一个整数D,为传送深度上限。
  输入文件读入至文件末(EOF)结束。
 
【输出格式】
 
  对于每组数据,输出一个正整数,若消息不能成功发布,则输出-1,否则输出发布成功所需的最少时间。
 
【输入样例】
 
【样例1】
 4
 0 1 8 3
 1 0 2 7
 8 2 0 9
 3 7 9 0
 2 2 2 2
 3


【样例2】
 4
 0 1 8 3
 1 0 2 7
 8 2 0 9
 3 7 9 0
 1 2 2 2
 3


【样例3】
 4
 0 3 2 1
 3 0 1 2
 2 1 0 2
 1 2 2 0
 2 2 2 2
 1
 
【输出样例】
 
【样例1】
 4


【样例2】
 10


【样例3】
 -1
 
【样例解释】
 
  样例1:P[0]先发P[1]耗时1;P[0]发给P[3]的同时P[1]发给P[2],分别耗时3和2;一共需要耗时4。


  样例2:P[0]先发P[1]耗时1;因为P[0]最多只能转发一次,所以P[0]不能再发送;然后P[1]相继转发给P[2]和P[3]的同,分别耗时2和7;一共需要耗时10。 


  样例3:因为受到有限制工作量和传送深度的限制,至少有一人最终不能获知消息,故输出-1。 
 
【数据范围】
 

1<=n<=15



分析:

哎哟。。。做的时候真的是各种奇奇怪怪的状况都有,最开始把时间的概念理解错了,后来终于步入正轨(滑稽)之后发现自己怎么样都要超时,第一次的思路是给每一个点找父亲,但是为了保证这个思路的正确性要先生成一次排列。。。(N=15的时候排列都生成不完还想AC??),后来想了想一次给所有得到消息的通信员找儿子呢?(集合*排列?手动再见)

最后,终于得到了如下思路:

对于当前的一个得到消息的点,枚举它的下一步要做什么:有两种选择,其一是继续传递信息,其二是不再继续传递信息,然后就变成了一个单纯的排列问题(一个点只收到一次信息,多了浪费时间)。加上题目本身有很多的约束条件,只要加上一个最优性剪枝就足够快了。

还有一件事,特判一下所有的C[i]的和是否大于等于N,是的话才回溯,不然的话是出不来的。(当然完全可以写成一个剪枝)


总结:

1、状态函数的设计。当最明显的状态操作起来过于复杂的时候,可以分解这个状态,通过更加简单的状态进行转移。

2、状态设计真的很重要!!!状态设计真的很重要!!!状态设计真的很重要!!!


思路大概就是这样,其他的细节参见代码。


AC代码:

#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#include<cmath>#include<algorithm>#include<vector>#define INF 1e9+5 using namespace std;const int maxn=20;int N,T[maxn][maxn],C[maxn],D;int vis[maxn],dist[maxn],dep[maxn],A[maxn];int ans;void data_in(){ans=INF;memset(vis,0,sizeof(vis));memset(A,0,sizeof(A));memset(dist,0,sizeof(dist));memset(dep,0,sizeof(dep));for(int i=0;i<N;i++)for(int j=0;j<N;j++)scanf("%d",&T[i][j]);for(int i=0;i<N;i++) scanf("%d",&C[i]);scanf("%d",&D);}void run(int i,int tot,int nowt)//现在有tot个人知道消息,考察第i个通信员的下一步行动,时间为nowt {if(tot>=N){if(nowt<ans) ans=nowt;return;}if(nowt>=ans) return;int x=A[i];if(C[x]>0 && dep[x]<D){for(int y=1;y<N;y++) if(!vis[y]){dist[y]=dist[x]+T[x][y];dist[x]=dist[y];C[x]--;dep[y]=dep[x]+1;A[tot+1]=y;vis[y]=1;run(i,tot+1,max(dist[y],nowt));dep[y]=0;C[x]++;dist[x]-=T[x][y];dist[y]=0;vis[y]=0;}}if(i<tot) run(i+1,tot,nowt);}int main(){freopen("test.in","r",stdin);freopen("test.out","w",stdout);while(scanf("%d",&N)==1){data_in();vis[0]=1;A[1]=0;int sum=0;for(int i=0;i<N;i++) sum+=C[i];if(sum>=N || N==1) run(1,1,0);if(ans==INF) ans=-1;printf("%d\n",ans);}return 0;}

阅读全文
0 0
原创粉丝点击