uva 515 King
這個題目,做之前不知道什么事差束約分,看完題目上床睡覺,想了半個小時,想到了要判斷一個有向圖有沒有負環。第二天起來再想,覺得是判斷有沒有環(包括負環,正環,0環),但是不確定,要真的是這樣的話,要權值來干什么,直接拓撲排序就好了,然后不確定,找了解題報告瞄一下,個個都是差束約分,然后沒看其他內容,直接去學差束約分,看算法導論和網上的資料看了一個小時,懂了,就寫,其實就是差束約分的模板題
?
?說說思路和題目:給你一個長度為n的序列,從下標1到n標號,然后給你m個連續的子序列,讓你求和,和要大于或者小于給定的k,如果這m個不等式都能成立,就輸出成功,只要有一個不成立就輸出不成立。sample中第一個就是成立的,第二個不成立
然后我們先來設定一個值Xi,Xi=a1+a2+a3……+ai ; 所以輸入中是給出一個下標i和長度l,連續的子序列就是 ai+(ai+1)+(ai+2)……(ai+l)
那么顯然就是等于 ?(Xi+l)-(Xi-1) ?(長的連續和減去短的連續和,得到中間的部分),這樣我們就構建出了差束約分里面的不等式
所以可以知道,雖然a序列標號是從1到n,但是X序列顯然是從0到n標號的(想想就知道)
另外一個轉化問題就是大于(小于)轉化為大于等于(小于等于),因為說了所有數字都是整數所以這個轉化很容易的
Xj-Xi>k ? 等價于 ?Xj-Xi >= k+1 ? ? Xj-Xi<k ?等價于 Xj-Xi <= k-1 ? ? ?(因為都是整數所以可以這樣做)
然后就是差束約分的有向圖建圖,這里是用了鄰接表建圖,可以處理掉平行邊
最后一步,設置一個新源點Xn+1 ?(別忘了是從0到n標號的,所以只能設置n+1),和其余所有出現過的點相連,都是有向邊,從Xn+1指向其他點,權值都是0
所以這樣建圖后,整個圖一定是連通的,在沒有負環的情況下一定有可行解,然后就是spfa求最短路,順便判斷負環,有負環就沒有可行解,否則就有
(其實普通的差束約分問題,只要沒有負環就有可行解,而且有一組的和就會有無數組)
?
還有,這道題就是判斷有沒有可行解,其實就是判斷有沒有負環(原來就是我昨天晚上想的那樣,不過那時候不懂什么是差束約分也證明不了),由于是有向圖而且是有負權那當然是spfa,spfa有bfs和dfs版本,兩個都寫在代碼里面,按理論講,單單判有沒有負環的話dfs更好,不過這道題bfs和dfs的時間一樣,都跑出了0.072,驚奇的是沖進去了第一名,是我第一次沖進去uva的第一名
多少要紀念一下啊,畢竟做了uva大半年了第一次沖進第一名(上一次最好成績是第5名,也是spfa_dfs判負環)
不過怎么說呢…………這道還是水題,而且能算是模板題吧
鄰接表+spfa判負環
?
#include <cstdio> #include <cstring> #include <queue> using namespace std; #define N 110 #define M 110 #define INF 0x3f3f3f3f struct edge {int u,v,w,next; }e[M+N]; //這個數組不能只開到M,因為后面要加入一個新源點n+1,與0到n的所有頂點連接一條有向邊 int first[N]; int used[N]; //在輸入中出現過哪些點 int vis[N],c[N]; //spfa_dfs起標記作用 int d[N]; //最短路徑數組 int n,m,edgenum,s; //s是源點也就是0,edgenum是邊集數組的條數void input() {memset(used,0,sizeof(used));memset(first,-1,sizeof(first));scanf("%d",&m);edgenum=0;for(int k=0; k<m; k++) //讀入所有不等式,轉化為邊的信息,但注意邊數是edgenum不是k {int i,j,w; char op[5];scanf("%d%d%s%d",&i,&j,op,&w);j+=i; i--; //得到頂點i,j,j標號一定大于iused[i]=used[j]=1; //標記出現過這些點if(!strcmp(op,"lt")) //是小于 {w--; //相當于<=w-1e[edgenum].u=i;e[edgenum].v=j;e[edgenum].w=w;e[edgenum].next=first[i];first[i]=edgenum;edgenum++;}else //大于 {w++; //相當于>=w+1e[edgenum].u=j;e[edgenum].v=i;e[edgenum].w=-w;e[edgenum].next=first[j];first[j]=edgenum;edgenum++;}}used[n+1]=1;for(int i=0; i<=n; i++) if(used[i]) //新設置一個源點0,跟所有已有的點相連 {e[edgenum].u=n+1;e[edgenum].v=i;e[edgenum].w=0;e[edgenum].next=first[n+1];first[n+1]=edgenum;edgenum++;}return ; }void print_graph() {printf("鄰接表\n");for(int i=0; i<=n+1; i++) if(used[i]){printf("%d:_____________________\n",i);for(int k=first[i]; k!=-1; k=e[k].next)printf("%d\\%d\n",e[k].v,e[k].w);}printf("*********\n");printf("打印邊集數組\n");for(int i=0; i<edgenum; i++)printf("%d %d %d %d\n",e[i].u,e[i].v,e[i].w,e[i].next);printf("*********\n");return ; }int spfa_dfs(int u) {vis[u]=1;for(int k=first[u]; k!=-1; k=e[k].next) //遍歷頂點u的鄰接表 {int v=e[k].v , w=e[k].w;if( d[u]+w < d[v]){ d[v]=d[u]+w;if(!vis[v]){if(spfa_dfs(v))return 1;}else return 1;}}vis[u]=0;return 0; } int spfa_bfs(int s) {int flag=1;queue <int> q;memset(c,0,sizeof(c)); c[s]++;memset(d,0x3f,sizeof(d)); d[s]=0;memset(vis,0,sizeof(vis)); vis[s]=1;q.push(s);while(!q.empty()){int u=q.front(); vis[u]=0; q.pop();for(int k=first[u]; k!=-1; k=e[k].next){int v=e[k].v , w=e[k].w;if(d[u]+w<d[v]){d[v]=d[u]+w;if(!vis[v]){vis[v]=1;q.push(v);c[v]++;if(c[v]>n+1)return 1; //找到負環 }}}}return 0; //沒有負環 } void judge() {s=n+1; //源點memset(d,0x3f,sizeof(d)); d[s]=0;memset(vis,0,sizeof(vis));//int tmp=spfa_dfs(s); int tmp=spfa_bfs(s);//spfa的dfs版本和bfs版本都有了,都可以AC,注釋掉換過來就可以了if(tmp)printf("successful conspiracy\n");elseprintf("lamentable kingdom\n");return ; } int main() {while(scanf("%d",&n) && n){input();//print_graph(); //測試函數 judge(); }return 0; }轉載于:https://www.cnblogs.com/scau20110726/archive/2012/11/29/2795153.html
總結
以上是生活随笔為你收集整理的uva 515 King的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: IOS UIViewController
- 下一篇: PPP+L2TP