【查分约束】我爱你啊
某看了秒5而内心激动的人创作本题
我爱你啊
【题目描述】
呐,贵树真的是一个很帅的男孩子呢,所以好多女孩都给他写至少一封
了情书。那每个女孩给了贵树写了多少情书呢?我们不知道,但是我们知
道一些女孩子写情书数量的关系,你的任务是推断出贵树最少受到了多少
情书。
【输入文件】
输入的第一行为两个整数 N,K,表示一共 N 个女孩,知道 K 对关系
接下来 K 行,每行三个整数 t,A,B
如果 t=1,则表示 A 的情书和 B 的情书数量一样
如果 t=2,则表示 A 的情书少于 B 的情书数量
如果 t=3,则表示 A 的情书不少于 B 的情书数量
如果 t=4,则表示 A 的情书多于 B 的情书数量
如果 t=5,则表示 A 的情书不多于 B 的情书数量
【输出文件】
一行,表示贵树满足这些关系时最少情况下收到情书的数量。
如果这些关系不可能同时被满足,则输出 -1。
【样例输入】
5 7
1 1 2
2 3 2
4 4 1
3 4 5
5 4 5
2 3 5
4 5 1
【样例输出】
11
【数据规模】
对于 30% 数据,保证 N <=100
对于 100% 数据,保证 N <=100000
考试时拿到这道题,已经想到了spfa算法,但是_(:з」∠)_因为没有学习查分约束,所以没有过(水了十分ORZ)
查分约束,如本题的这种问题类型。本质就是不等式组,求是否有解,或者解是多少的问题,神奇的是这是一个披着数学皮的……图论。
比如给出几个不等式:A-B>=q,B-C>=p,C-A<=r,求C-A的最大值
易得C-A的最大值是比较p+q和r的结果;
当我们把上面的问题转化为图时,如下
显然这是一个有向图,这个不等式的解就是A到C的最短距离(为什么是最短?因为在确定每一条边的长度时,我们已经选择了最长的情况,现在是在确定这种方案是否可行,如,若r>p+q,我们选择r作为解,反之选择(p+q)作为解)
***当求最大值时,需要把不等式都转化为x-y<=z的形式,寻找图中的最短路;当求最小值时,需要把不等式转化为x-y>=z的形式,寻找最长路
回到最初的问题上,显然我们已经把问题转化为求UI长路问题了,而且每一个点的最长距离就是它的最小值;
代码如下
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 int n,k; 6 struct node{ 7 int to,next; 8 int value; 9 }e[100001]; 10 int head[100001]; 11 int m=0; 12 int queue[100001]; 13 int q_head=0; 14 int q_tail=1; 15 int dis[100001]; 16 bool v[100001]; 17 void add(int u,int v,int w)//加边 18 { 19 m++; 20 e[m].to=v; 21 e[m].next=head[u]; 22 e[m].value=w; 23 head[u]=m; 24 } 25 void read() 26 { 27 memset(v,0,sizeof(v)); 28 memset(dis,0,sizeof(dis)); 29 scanf("%d%d",&n,&k); 30 for(int i=1;i<=n;i++) add(0,i,0); 31 for(int i=1;i<=k;i++) 32 { 33 int a,b,t; 34 scanf("%d%d%d",&t,&a,&b); 35 if(t==1) 36 { 37 add(a,b,0); 38 add(b,a,0); 39 }//如果相等,这条边就是一条无向边,不影响最终结果 ; 40 if(t==3) 41 { 42 add(b,a,0); 43 add(b,a,1); 44 } 45 if(t==5) 46 { 47 add(a,b,0); 48 add(a,b,1); 49 } 50 if(t==2) add(a,b,1); 51 if(t==4) add(b,a,1);//注意括号里a,b顺序的不同,这表现了不等式形式(x-y<=z→→y-x>=-z)的变化; 52 } 53 } 54 void spfa()//寻找最长路 ; 55 { 56 dis[0]=0;//因为找起点是很麻烦的,所以我们虚拟一个点 0 作为起点,使每个点到它的距离为 0,这样依然不影响结果; 57 v[0]=1; 58 queue[q_tail]=0; 59 while(q_tail>q_head) 60 { 61 int p=queue[++q_head]; 62 v[p]=1; 63 int p1=head[p]; 64 while(p1!=0) 65 { 66 if(dis[e[p1].to]<=dis[p]+e[p1].value) 67 { 68 dis[e[p1].to]=dis[p]+e[p1].value; 69 if(!v[e[p1].to]) 70 { 71 queue[++q_tail]=e[p1].to; 72 v[e[p1].to]=1; 73 } 74 } 75 p1=e[p1].next; 76 } 77 } 78 int ans=n;//因为每个人至少一封,所以ans最小为 n ; 79 for(int i=1;i<=n;i++) 80 printf("i=%d dis=%d\n",i,dis[i]); 81 for(int i=1;i<=n;i++) 82 ans+=dis[i]; // 加上每一个点的最小值; 83 printf("%d ",ans); 84 } 85 int main() 86 { 87 read(); 88 spfa(); 89 return 0; 90 }