下面是算是难的了吧。。。(不知道还搞得掉吗!)
先上这个。。。
昂贵的聘礼 POJ 1062
最近比较浮躁。看了好久才懂的。。。
首先题已经知道起点!限制条件:因为有等级制度的限制。可用枚举(一个一个物品列出来)
之后发现是单向图问题!
先看代码吧。
1 #include<iostream> 2 #include<stdio.h> 3 #include<algorithm> 4 using namespace std; 5 int max1,a[105][105],level[105],d[105],m,vis[105]; 6 int distesila() 7 { 8 int i,min1,j,p; 9 for(i=1;i<=m;i++) 10 d[i]=a[0][i]; 11 for(i=1;i<=m;i++) 12 { 13 min1=1000005; 14 for(j=1;j<=m;j++) 15 if(!vis[j]&&d[j]<min1) 16 { 17 min1=d[j]; 18 p=j; 19 } 20 vis[p]=1;//并巧妙地和该算法已有的数组结合起来 21 for(j=1;j<=m;j++) 22 { 23 if(!vis[j]&&d[j]>d[p]+a[p][j]) 24 d[j]=a[p][j]+d[p]; 25 } 26 } 27 return d[1]; 28 } 29 int main() 30 { 31 int max1,max2,n,i,j,p,a1,a2,b2,min1level,sd; 32 max1=10000005; 33 while(scanf("%d%d",&n,&m)!=EOF) 34 { 35 for(i=0;i<=m;i++) 36 for(j=0;j<=m;j++) 37 a[i][j]=max1; 38 for(i=0;i<=m;i++) 39 a[i][i]=0; 40 for(i=1;i<=m;i++) 41 { 42 scanf("%d%d%d",&p,&level[i],&a1); 43 while(a1--) 44 { 45 scanf("%d%d",&a2,&b2); 46 a[a2][i]=b2; 47 } 48 a[0][i]=p;//既然题目中没有明确的起点且值不会小于1的,就标志为0了 49 } 50 max2=100005; 51 for(i=1;i<=m;i++) 52 { 53 min1level=level[i];//假定为最小的进行枚举 54 for(j=1;j<=m;j++) 55 { 56 if(level[j]-min1level>n||level[j]<min1level) 57 vis[j]=1;//判断并用数组进行储存该情况 58 else 59 vis[j]=0; 60 } 61 sd=distesila(); 62 max2=min(max2,sd); 63 }//此处用枚举把每种可能算出来,取最小值 64 printf("%d ",max2); 65 } 66 return 0; 67 }
经过三天的洗礼:终于看懂了。。。(!!!)
先讲一下题意:
有n个房间,编号1~n,小明要从1走到n。
接下给出n排:
第一个数 :到达此房间可获得的能量(100~-100) 岔口数(与哪些房间相连) 编号
注意:起点与终点获得的能量为0.初始能量为100.且每个房间只能获得一次。
因为还不知道什么是正环和负环。。。(相对于最长路与最短路来说)
首先这个题目应该求最长路吧。。。(到终点能量为正啊)
那么对应的正环就出现了,(谁会想到一开始判断1到n是否有房间连接。。。)
但是出现了正环的话,表示着什么呢?
反正求最短路时,还没遇到值为负的。。。就不清楚负环的含义。。。
看了百度百科才发现出现负环表明无最短路径。。。条件是:某点进入队列超过n次!
那么对应的正环一旦出现的话,能量对应的就为很大很大了。。。之后就判断此点到终点能不能有房间联通就行了。。。此刻就需要上面没有想到的(谁会想到一开始判断1到n是否有房间连接。。。)
那么就顺便在一开始判断1到n是否有房间连接。。。
那么关键还是理解spfa算法和负环。。。
1 #include<iostream> 2 #include<queue> 3 #include<string.h> 4 #include<algorithm> 5 #include<stdio.h> 6 const int MAXN=110; 7 using namespace std; 8 int n; 9 int weight[MAXN]; 10 int power[MAXN]; 11 int _count[MAXN];//记录某点的入队列次数 12 bool map[MAXN][MAXN];//区分于reach[]的用法 13 bool reach[MAXN][MAXN];//floyd判断任何两点是否可达 14 15 16 void Floyd() 17 { 18 for(int k=1;k<=n;k++) 19 for(int i=1;i<=n;i++) 20 for(int j=1;j<=n;j++) 21 reach[i][j]=(reach[i][j]||(reach[i][k]&&reach[k][j])); 22 } 23 24 bool SPFA(int now) 25 { 26 queue<int>Q; 27 Q.push(now); 28 memset(power,0,sizeof(power)); 29 memset(_count,0,sizeof(_count)); 30 power[1]=100; 31 while(!Q.empty()) 32 { 33 int now=Q.front(); 34 Q.pop(); 35 _count[now]++; 36 if(_count[now]>=n) 37 return reach[now][n];//如果某个点的次数超过n次,那么说明存在正环,此时只要判断这点到n点是否可达就行了 38 for(int next=1;next<=n;next++) 39 if(map[now][next]&&power[now]+weight[next]>power[next]&&power[now]+weight[next]>0)//避免有负数 40 { 41 Q.push(next); 42 power[next]=power[now]+weight[next]; 43 } 44 } 45 return power[n]>0; 46 }//这里少了一个判断是否入列的数组,但没关系,不过时间较长一些 47 48 int main() 49 { 50 while(~scanf("%d",&n)&&n!=-1) 51 { 52 memset(map,false,sizeof(map)); 53 memset(reach,false,sizeof(reach)); 54 for(int i=1;i<=n;i++) 55 { 56 int num; 57 scanf("%d%d",&weight[i],&num); 58 while(num--) 59 { 60 int k; 61 scanf("%d",&k); 62 map[i][k]=true; 63 reach[i][k]=true; 64 } 65 } 66 Floyd(); 67 if(!reach[1][n])//如果起点到终点没有房间连接 68 printf("hopeless "); 69 else 70 { 71 if(SPFA(1)) 72 printf("winnable "); 73 else 74 printf("hopeless "); 75 } 76 } 77 return 0; 78 }