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。
【数据范围】
有 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
- Vijos P1324 最优组播树(这再一次证明了状态函数设置恰当的重要性)
- 哈夫曼树最优性的证明(思考良久)
- [jzoj]1228. Matrix(贪心的最优性证明)
- 8-puzzle 可解性的证明 & 最优解性的证明
- 证明哈夫曼编码是最优的
- 再一次进入找工作状态
- 如何用最恰当的方式做出正确的选择和最优地解决问题
- VIJOS 1754 最优贸易
- vijos-1754 最优贸易
- 【定下吧,我忘了】利用微分中值定理证明函数(我思考的解法)
- JS中eval()函数的重要性
- 虚析构函数的重要性
- C函数的重要性
- 虚析构函数的重要性
- 【最大费用流】【最优匹配】丘比特的烦恼 Vijos 1169
- 水壶再一次爆了!!!
- 再一次选择了ubuntu
- Vijos P1881 闪烁的繁星 (自己加强了一下。。)
- 算法:两种算法相比较(选择和插入排序)
- HDU 6034 Balala Power!【贪心】
- Hashtable源码解析
- java未来趋势 Java促进大数据的大发展
- Database UVA
- Vijos P1324 最优组播树(这再一次证明了状态函数设置恰当的重要性)
- 面试题:软件测试,如何测微信的朋友圈?
- App运行时发生OOM的原因你知道几种?应该如何避免?
- 成为Java顶尖程序员 ,看这11本书就够了
- HDU-1171-Big Event in HDU(01背包的简单变形)
- Android_Service获取系统服务
- Eclipse插件之动态刷新ToolBar的图片
- MA8601|HUB方案|中文规格书|MA8601一级代理|MA8601应用|
- tomcat连接数、线程数关系