青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

JulyRina's blog
welcome to July Rina's blog
posts - 22,comments - 1,trackbacks - 0

題目

有N種物品和一個容量為V的背包,每種物品都有無限件可用。第i種物品的費用是c[i],價值是w[i]。求解將哪些物品裝入背包可使這些物品的費用總和不超過背包容量,且價值總和最大。

基本思路

這個問題非常類似于01背包問題,所不同的是每種物品有無限件。也就是從每種物品的角度考慮,與它相關的策略已并非取或不取兩種,而是有取0件、取1件、取2件……等很多種。如果仍然按照解01背包時的思路,令f[i][v]表示前i種物品恰放入一個容量為v的背包的最大權值。仍然可以按照每種物品不同的策略寫出狀態轉移方程,像這樣:

f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v}

這跟01背包問題一樣有O(VN)個狀態需要求解,但求解每個狀態的時間已經不是常數了,求解狀態f[i][v]的時間是O(v/c[i]),總的復雜度可以認為是O(V*Σ(V/c[i])),是比較大的。

將01背包問題的基本思路加以改進,得到了這樣一個清晰的方法。這說明01背包問題的方程的確是很重要,可以推及其它類型的背包問題。但我們還是試圖改進這個復雜度。

一個簡單有效的優化

完全背包問題有一個很簡單有效的優化,是這樣的:若兩件物品i、j滿足c[i]<=c[j]且w[i]>=w[j],則將物品j去掉,不用考慮。這個優化的正確性顯然:任何情況下都可將價值小費用高得j換成物美價廉的i,得到至少不會更差的方案。對于隨機生成的數據,這個方法往往會大大減少物品的件數,從而加快速度。然而這個并不能改善最壞情況的復雜度,因為有可能特別設計的數據可以一件物品也去不掉。

這個優化可以簡單的O(N^2)地實現,一般都可以承受。另外,針對背包問題而言,比較不錯的一種方法是:首先將費用大于V的物品去掉,然后使用類似計數排序的做法,計算出費用相同的物品中價值最高的是哪個,可以O(V+N)地完成這個優化。這個不太重要的過程就不給出偽代碼了,希望你能獨立思考寫出偽代碼或程序。

轉化為01背包問題求解

既然01背包問題是最基本的背包問題,那么我們可以考慮把完全背包問題轉化為01背包問題來解。最簡單的想法是,考慮到第i種物品最多選V/c[i]件,于是可以把第i種物品轉化為V/c[i]件費用及價值均不變的物品,然后求解這個01背包問題。這樣完全沒有改進基本思路的時間復雜度,但這畢竟給了我們將完全背包問題轉化為01背包問題的思路:將一種物品拆成多件物品。

更高效的轉化方法是:把第i種物品拆成費用為c[i]*2^k、價值為w[i]*2^k的若干件物品,其中k滿足c[i]*2^k<=V。這是二進制的思想,因為不管最優策略選幾件第i種物品,總可以表示成若干個2^k件物品的和。這樣把每種物品拆成O(log V/c[i])件物品,是一個很大的改進。

但我們有更優的O(VN)的算法。

O(VN)的算法

這個算法使用一維數組,先看偽代碼:

for i=1..N     
   for v=0..V
      f[v]=max{f[v],f[v-cost]+weight}

你會發現,這個偽代碼與P01的偽代碼只有v的循環次序不同而已。為什么這樣一改就可行呢?首先想想為什么P01中要按照v=V..0的逆序來循環。這是因為要保證第i次循環中的狀態f[i][v]是由狀態f[i-1][v-c[i]]遞推而來。換句話說,這正是為了保證每件物品只選一次,保證在考慮“選入第i件物品”這件策略時,依據的是一個絕無已經選入第i件物品的子結果f[i-1][v-c[i]]。而現在完全背包的特點恰是每種物品可選無限件,所以在考慮“加選一件第i種物品”這種策略時,卻正需要一個可能已選入第i種物品的子結果f[i][v-c[i]],所以就可以并且必須采用v=0..V的順序循環。這就是這個簡單的程序為何成立的道理。

值得一提的是,上面的偽代碼中兩層for循環的次序可以顛倒。這個結論有可能會帶來算法時間常數上的優化。

這個算法也可以以另外的思路得出。例如,將基本思路中求解f[i][v-c[i]]的狀態轉移方程顯式地寫出來,代入原方程中,會發現該方程可以等價地變形成這種形式:

f[i][v]=max{f[i-1][v],f[i][v-c[i]]+w[i]}

將這個方程用一維數組實現,便得到了上面的偽代碼。

最后抽象出處理一件完全背包類物品的過程偽代碼:

procedure CompletePack(cost,weight)     
   for v=cost..V
      f[v]=max{f[v],f[v-c[i]]+w[i]}

總結

完全背包問題也是一個相當基礎的背包問題,它有兩個狀態轉移方程,分別在“基本思路”以及“O(VN)的算法“的小節中給出。希望你能夠對這兩個狀態轉移方程都仔細地體會,不僅記住,也要弄明白它們是怎么得出來的,最好能夠自己想一種得到這些方程的方法。事實上,對每一道動態規劃題目都思考其方程的意義以及如何得來,是加深對動態規劃的理解、提高動態規劃功力的好方法。

posted @ 2015-02-18 20:31 JulyRina 閱讀(413) | 評論 (0)編輯 收藏

題目

有N件物品和一個容量為V的背包。第i件物品的費用是c[i],價值是w[i]。求解將哪些物品裝入背包可使價值總和最大。

基本思路

這是最基礎的背包問題,特點是:每種物品僅有一件,可以選擇放或不放。

用子問題定義狀態:即f[i][v]表示前i件物品恰放入一個容量為v的背包可以獲得的最大價值。則其狀態轉移方程便是:

f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}

這個方程非常重要,基本上所有跟背包相關的問題的方程都是由它衍生出來的。所以有必要將它詳細解釋一下:“將前i件物品放入容量為v的背包中”這個子問題,若只考慮第i件物品的策略(放或不放),那么就可以轉化為一個只牽扯前i-1件物品的問題。如果不放第i件物品,那么問題就轉化為“前i-1件物品放入容量為v的背包中”,價值為f[i-1][v];如果放第i件物品,那么問題就轉化為“前i-1件物品放入剩下的容量為v-c[i]的背包中”,此時能獲得的最大價值就是f[i-1][v-c[i]]再加上通過放入第i件物品獲得的價值w[i]。

優化空間復雜度

以上方法的時間和空間復雜度均為O(VN),其中時間復雜度應該已經不能再優化了,但空間復雜度卻可以優化到O。

先考慮上面講的基本思路如何實現,肯定是有一個主循環i=1..N,每次算出來二維數組f[i][0..V]的所有值。那么,如果只用一個數組f[0..V],能不能保證第i次循環結束后f[v]中表示的就是我們定義的狀態f[i][v]呢?f[i][v]是由f[i-1][v]和f[i-1][v-c[i]]兩個子問題遞推而來,能否保證在推f[i][v]時(也即在第i次主循環中推f[v]時)能夠得到f[i-1][v]和f[i-1][v-c[i]]的值呢?事實上,這要求在每次主循環中我們以v=V..0的順序推f[v],這樣才能保證推f[v]時f[v-c[i]]保存的是狀態f[i-1][v-c[i]]的值。偽代碼如下:

for i=1..N     
   for v=V..0
      f[v]=max{f[v],f[v-c[i]]+w[i]};

其中的f[v]=max{f[v],f[v-c[i]]}一句恰就相當于我們的轉移方程f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]},因為現在的f[v-c[i]]就相當于原來的f[i-1][v-c[i]]。如果將v的循環順序從上面的逆序改成順序的話,那么則成了f[i][v]由f[i][v-c[i]]推知,與本題意不符,但它卻是另一個重要的背包問題P02最簡捷的解決方案,故學習只用一維數組解01背包問題是十分必要的。

事實上,使用一維數組解01背包的程序在后面會被多次用到,所以這里抽象出一個處理一件01背包中的物品過程,以后的代碼中直接調用不加說明。

過程ZeroOnePack,表示處理一件01背包中的物品,兩個參數cost、weight分別表明這件物品的費用和價值。

procedure ZeroOnePack(cost,weight)     
   for v=V..cost
      f[v]=max{f[v],f[v-cost]+weight}

注意這個過程里的處理與前面給出的偽代碼有所不同。前面的示例程序寫成v=V..0是為了在程序中體現每個狀態都按照方程求解了,避免不必要的思維復雜度。而這里既然已經抽象成看作黑箱的過程了,就可以加入優化。費用為cost的物品不會影響狀態f[0..cost-1],這是顯然的。

有了這個過程以后,01背包問題的偽代碼就可以這樣寫:

for i=1..N     
   ZeroOnePack(c[i],w[i]);

初始化的細節問題

我們看到的求最優解的背包問題題目中,事實上有兩種不太相同的問法。有的題目要求“恰好裝滿背包”時的最優解,有的題目則并沒有要求必須把背包裝滿。一種區別這兩種問法的實現方法是在初始化的時候有所不同。

如果是第一種問法,要求恰好裝滿背包,那么在初始化時除了f[0]為0其它f[1..V]均設為-∞,這樣就可以保證最終得到的f[N]是一種恰好裝滿背包的最優解。

如果并沒有要求必須把背包裝滿,而是只希望價格盡量大,初始化時應該將f[0..V]全部設為0。

為什么呢?可以這樣理解:初始化的f數組事實上就是在沒有任何物品可以放入背包時的合法狀態。如果要求背包恰好裝滿,那么此時只有容量為0的背包可能被價值為0的nothing“恰好裝滿”,其它容量的背包均沒有合法的解,屬于未定義的狀態,它們的值就都應該是-∞了。如果背包并非必須被裝滿,那么任何容量的背包都有一個合法解“什么都不裝”,這個解的價值為0,所以初始時狀態的值也就全部為0了。

這個小技巧完全可以推廣到其它類型的背包問題,后面也就不再對進行狀態轉移之前的初始化進行講解。

一個常數優化

前面的偽代碼中有 for v=V..1,可以將這個循環的下限進行改進。

由于只需要最后f[v]的值,倒推前一個物品,其實只要知道f[v-w[n]]即可。以此類推,對以第j個背包,其實只需要知道到f[v-sum{w[j..n]}]即可,即代碼中的

for i=1..N     
   for v=V..0

可以改成

for i=1..n     
   bound=max{V-sum{w[i..n]},c[i]}
   for v=V..bound

這對于V比較大時是有用的。

小結

01背包問題是最基本的背包問題,它包含了背包問題中設計狀態、方程的最基本思想,另外,別的類型的背包問題往往也可以轉換成01背包問題求解。故一定要仔細體會上面基本思路的得出方法,狀態轉移方程的意義,以及最后怎樣優化的空間復雜度。

posted @ 2015-02-18 20:30 JulyRina 閱讀(307) | 評論 (0)編輯 收藏
題目大意:求圖上單點到單點之間的最短路。
題目分析:SPFA(Shortest Path Faster Algorithm)是Bellman-Ford算法的一種隊列實現,減少了不必要的冗余計算。
算法大致流程是用一個隊列來進行維護。 初始時將源加入隊列。 每次從隊列中取出一個元素,并對所有與他相鄰的點進行松弛,若某個相鄰的點松弛成功,則將其入隊。 直到隊列為空時算法結束。
這個算法,簡單的說就是隊列優化的bellman-ford,利用了每個點不會更新次數太多的特點發明的此算法
SPFA——Shortest Path Faster Algorithm,它可以在O(kE)的時間復雜度內求出源點到其他所有點的最短路徑,可以處理負邊。SPFA的實現甚至比Dijkstra或者Bellman_Ford還要簡單:
設Dist代表S到I點的當前最短距離,Fa代表S到I的當前最短路徑中I點之前的一個點的編號。開始時Dist全部為+∞,只有Dist[S]=0,Fa全部為0。
維護一個隊列,里面存放所有需要進行迭代的點。初始時隊列中只有一個點S。用一個布爾數組記錄每個點是否處在隊列中。
每次迭代,取出隊頭的點v,依次枚舉從v出發的邊v->u,設邊的長度為len,判斷Dist[v]+len是否小于Dist[u],若小于則改進Dist[u],將Fa[u]記為v,并且由于S到u的最短距離變小了,有可能u可以改進其它的點,所以若u不在隊列中,就將它放入隊尾。這樣一直迭代下去直到隊列變空,也就是S到所有的最短距離都確定下來,結束算法。若一個點入隊次數超過n,則有負權環。
SPFA 在形式上和寬度優先搜索非常類似,不同的是寬度優先搜索中一個點出了隊列就不可能重新進入隊列,但是SPFA中一個點可能在出隊列之后再次被放入隊列,也就是一個點改進過其它的點之后,過了一段時間可能本身被改進,于是再次用來改進其它的點,這樣反復迭代下去。設一個點用來作為迭代點對其它點進行改進的平均次數為k,有辦法證明對于通常的情況,k在2左右。
SPFA算法(Shortest Path Faster Algorithm),也是求解單源最短路徑問題的一種算法,用來解決:給定一個加權有向圖G和源點s,對于圖G中的任意一點v,求從s到v的最短路徑。 SPFA算法是Bellman-Ford算法的一種隊列實現,減少了不必要的冗余計算,他的基本算法和Bellman-Ford一樣,并且用如下的方法改進: 1、第二步,不是枚舉所有節點,而是通過隊列來進行優化 設立一個先進先出的隊列用來保存待優化的結點,優化時每次取出隊首結點u,并且用u點當前的最短路徑估計值對離開u點所指向的結點v進行松弛操作,如果v點的最短路徑估計值有所調整,且v點不在當前的隊列中,就將v點放入隊尾。這樣不斷從隊列中取出結點來進行松弛操作,直至隊列空為止。 2、同時除了通過判斷隊列是否為空來結束循環,還可以通過下面的方法: 判斷有無負環:如果某個點進入隊列的次數超過V次則存在負環(SPFA無法處理帶負環的圖)。
SPFA算法有兩個優化算法 SLF 和 LLL: SLF:Small Label First 策略,設要加入的節點是j,隊首元素為i,若dist(j)<dist(i),則將j插入隊首,否則插入隊尾。 LLL:Large Label Last 策略,設隊首元素為i,隊列中所有dist值的平均值為x,若dist(i)>x則將i插入到隊尾,查找下一元素,直到找到某一i使得dist(i)<=x,則將i出對進行松弛操作。 SLF 可使速度提高 15 ~ 20%;SLF + LLL 可提高約 50%。 在實際的應用中SPFA的算法時間效率不是很穩定,為了避免最壞情況的出現,通常使用效率更加穩定的Dijkstra算法。
#include <cstdio>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
#define INF (1<<29)
const int maxn = 1010;

typedef pair<intint> P;
vector<P> G[maxn];
queue<int> que;
int V, E, dist[maxn];
bool vis[maxn];

void spfa(int s) {
    fill(dist, dist+V, INF);
    fill(vis, vis+V, false);
    dist[s] = 0;
    while(!que.empty()) que.pop();
    que.push(s);
    while(!que.empty()) {
        int u = que.front();
        que.pop();
        vis[u] = false;
        int sz = G[u].size();
        for(int i=0;i<sz;i++) {
            int v = G[u][i].first;
            int w = G[u][i].second;
            if(dist[v] > dist[u] + w) {
                dist[v] = dist[u] + w;
                if(!vis[v]) {
                    vis[v] = true;
                    que.push(v);
                }
            }
        }
    }
}

int main() {
    scanf("%d%d" , &E, &V);
    for(int i=0;i<V;i++) G[i].clear();
    for(int i=0;i<E;i++) {
        int u, v, w;
        scanf("%d%d%d" , &u, &v, &w);
        u --; v --;
        G[u].push_back(make_pair(v, w));
        G[v].push_back(make_pair(u, w));
    }
    spfa(0);
    printf("%d\n", dist[V-1]);
    return 0;
}
posted @ 2015-02-13 19:42 JulyRina 閱讀(260) | 評論 (0)編輯 收藏
題目大意:求圖上單點到單點之間的最短路。
題目分析:之前我們在同一道問題上分析過了單源最短路問題的Dijkstra算法
使用鄰接矩陣實現的Dijkstra算法的時間復雜度是O(|V|^2)。使用鄰接表的話,更新最短距離只需要更新每一條邊即可,因此這部分的時間復雜度是O(|E|)。但是每次要枚舉所有的頂點來查找下一個使用的頂點,因此最終復雜度還是O(|V|^2)。在|E|比較小時,大部分經歷放在了查找下一次要是用的頂點上,因此需要使用合適的數據結構對其進行優化。
需要優化的是數值的插入(更新)和取出最小值兩個操作,因此使用堆就可以了。把每個頂點當前的最短距離用堆維護,在更新最短距離時,把對應的元素往根的方向移動以滿足堆的性質。而每次從堆中取出的最小值就是下一次要使用的頂點。這樣堆中元素共有O(|V|)個,更新和取出數值的操作有O(|E|)次,因此整個算法的復雜度是O(|E|log(V))
#include <cstdio>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
#define INF (1<<29)
const int maxn = 1010;

typedef pair<intint> P; //first是最短距離,second是頂點的編號
int V, dist[maxn];
vector<P> G[maxn];

void dijkstra(int s) {
    //通過指定greater<P>參數,堆按照first從小到大的順序取出值
    priority_queue<P, vector<P>, greater<P> > que;
    fill(dist, dist + V , INF);
    dist[s] = 0;
    while(!que.empty()) que.pop();
    que.push(P(0, s));
    while(!que.empty()) {
        P p = que.top(); que.pop();
        int u = p.second;
        if(dist[u] < p.first) continue;
        int sz = G[u].size();
        for(int i=0;i<sz;i++) {
            int to = G[u][i].second;
            int cost = G[u][i].first;
            if(dist[to] > dist[u] + cost) {
                dist[to] = dist[u] + cost;
                que.push(P(dist[to], to));
            }
        }
    }
}

int main() {
    int m;
    scanf("%d%d" , &m, &V);
    for(int i=0;i<V;i++) G[i].clear();
    for(int i=0;i<m;i++) {
        int from, to, cost;
        scanf("%d%d%d" , &from, &to, &cost);
        from --; to --;
        G[from].push_back(P(cost, to));
        G[to].push_back(P(cost, from));
    }
    dijkstra(0);
    printf("%d\n", dist[V-1]);
    return 0;
}
posted @ 2015-02-13 19:38 JulyRina 閱讀(350) | 評論 (0)編輯 收藏
題目大意:求圖上單點到單點之間的最短路。

題目分析:讓我們考慮沒有負邊的情況。在Bellman-Ford算法中,如果dist[i]還不是最短距離的話,那么即使進行dist[j]=dist[i]+(從i到j的邊的權值)的更新,dist[j]也不會變成最短距離。而且,即使dist[i]沒有變化,每一次循環也要檢查一遍從i出發的所有變。這顯然是很浪費時間的。因此可以對算法作如下修改。
(1)找到最短距離已經確定的頂點,從他出發更新相鄰頂點的最短距離。
(2)此后不再需要關心(1)中的“最短距離已經確定的頂點”。
在(1)和(2)中提到的“最短距離已經確定的”要怎么得到時問題的關鍵。在最開始時,只有起點的最短距離是確定的。而在尚未使用過的頂點中,距離dist[i]最小的頂點就會加入“最短距離已經確定的頂點”的陣營。這是因為由于不會存在負邊,所以dist[i]不會在之后的更新中變小。這個算法叫做Dijkstra算法。
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
#define INF (1<<29)
const int maxn = 1010;

typedef pair<intint> P;
vector<P> G[maxn];
int V, E, dist[maxn];
bool vis[maxn];

void dijkstra(int s) {
    fill(dist, dist + V, INF);
    fill(vis, vis + V, false);
    dist[s] = 0;
    while(true) {
        int u = -1;
        for(int i=0;i<V;i++)
            if(!vis[i] && (u == -1 || dist[i] < dist[u]))
                u = i;
        if(u == -1) break;
        vis[u] = true;
        int sz = G[u].size();
        for(int i=0;i<sz;i++) {
            int v = G[u][i].first;
            int w = G[u][i].second;
            dist[v] = min(dist[v], dist[u] + w);
        }
    }
}

int main() {
    scanf("%d%d" , &E, &V);
    for(int i=0;i<V;i++) G[i].clear();
    for(int i=0;i<E;i++) {
        int u, v, w;
        scanf("%d%d%d" , &u, &v, &w);
        u --; v --;
        G[u].push_back(make_pair(v, w));
        G[v].push_back(make_pair(u, w));
    }
    dijkstra(0);
    printf("%d\n", dist[V-1]);
    return 0;
}
posted @ 2015-02-13 19:34 JulyRina 閱讀(350) | 評論 (0)編輯 收藏
題目大意:求圖上單點到單點之間的最短路。

題目分析:單源最短路問題是固定一個起點,求它到其他所有點的最短路的問題。終點也固定問題叫做兩點之間最短路問題。但是因為單源最短路問題的復雜度是一樣的,因此通常當作單源最短路問題來求解。
記從起點s出發到頂點i的最短距離為dist[i]。則下述等式成立。
dist[i] = min{dist[j]+(從j到i的邊的權值)|e=(j,i)∈E}
如果給定的圖是一個DAG,就可以按托不許給頂點編號,并利用這條遞推關系計算出dist。但是,如果圖中有圈,就無法利用這樣的關系進行計算。
在這種情況下,記當前到頂點i的最短距離為dist[i],并設初值dist[s]=0,dist[i]=INF(足夠大的常數),再不斷使用這條地推關系式更新dist值,就可以算出新的dist。
只要途中不存在負圈,這樣的更新操作就是有限的。結束之后的最短操作就是所求的最短距離了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
#define INF (1<<29)
const int maxn = 1010, maxm = 4040;

int n, m;

struct Edge { int from, to, cost; } edge[maxm];
int V, E, dist[maxn];

void bellman_ford(int s) {
    for(int i=0;i<V;i++) dist[i] = INF;
    dist[s] = 0;
    while(true) {
        bool update = false;
        for(int i=0;i<E;i++) {
            Edge e = edge[i];
            if(dist[e.from] != INF && dist[e.to] > dist[e.from] + e.cost) {
                dist[e.to] = dist[e.from] + e.cost;
                update = true;
            }
        }
        if(!update) break;
    }
}

int main() {
    scanf("%d%d" , &m, &n);
    V = n; E = 2 * m;
    for(int i=0;i<E;i+=2) {
        Edge e;
        int from, to, cost;
        scanf("%d%d%d" , &from, &to, &cost);
        from --; to --;
        edge[i].from = from;
        edge[i].to = to;
        edge[i].cost = cost;
        edge[i+1].from = to;
        edge[i+1].to = from;
        edge[i+1].cost = cost;
    }
    bellman_ford(0);
    printf("%d", dist[n-1]);
    return 0;
}
這個算法叫做Bellman-Ford算法。如果在圖中不存在從s可達的負圈,那么最短路不會經過同一個頂點兩次(也就是說,最多通過|V|-1次),while(true)的循環最多經過|V|-1次,因此,復雜度是O(VE)。反之,如果存在從s可達的負圈,那么在第|V|次循環中也會更新dist的值,因此也可以用這個性質來檢查負圈。如果一開始對所有的i,都把dist[i]設為0,那么可以檢查出所有的負圈。
bool find_negetive_loop() {
    memset(dist, 0, sizeof(dist));
    
    for(int i=0;i<V;i++) {
        for(int j=0;j<E;j++) {
            if(dist[e.to] > dist[e.from] + e.cost) {
                dist[e.to] = dist[e.from] + e.cost;
                if(i == V-1) return true;
            }
        }
    }
    return false;
}
posted @ 2015-02-13 19:32 JulyRina 閱讀(251) | 評論 (0)編輯 收藏
題目大意:有N(N<100,000)個人要去M(M<10)個星球,每個人只可以去一些星球,一個星球最多容納Ki個人。請問是否所有人都可以選擇自己的星球
題目分析;直接建立二分圖模型,使用匈牙利算法。
    匈牙利算法可以解決多重匹配,原理和二分圖最大匹配很像。注意不要把可以匹配多個的點分割然后按照正常的二分匹配來做,那樣肯定會掛的。
    解決多重匹配就是記錄一下多重匹配的點(簡稱Y方點)已經匹配了Pi個點。如果Pi<Ki那么就直接上了,否則的話繼續搜索Yi已經匹配的每一個點并將Yi染色。
    因為Yi搜一次就需要染色了,而且Y方點最多是10個,所以每次找增廣路的深度最多是10,這樣就很快了。
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;

const int maxn = 100010;
const int maxm = 11;
int y_match[maxn][maxm], g[maxn][maxm], cnt[maxm], capacity[maxn], n, m;
bool vis[maxm];

bool dfs(int x) {
    for(int i=0;i<m;i++) {
        if(g[x][i] == 0 || vis[i] == truecontinue;
        vis[i] = true;
        if(cnt[i] < capacity[i]) {
            y_match[x][cnt[i]++] = x;
            return true;
        } else {
            for(int j=0;j<capacity[i];j++) {
                if(dfs(y_match[i][j]) == true) {
                    y_match[i][j] = x;
                    return true;
                }
            }
        }
    }
    return false;
}
bool hungary(int n) {
    for(int i=0;i<n;i++) {
        memset(vis, falsesizeof(bool)*(m));
        if(dfs(i) == false)
            return false;
    }
    return true;
}
int main() {
    while(~scanf("%d%d" , &n, &m)) {
        memset(cnt, 0, sizeof(int)*(n));
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
                scanf("%d", &g[i][j]);
        for(int i=0;i<m;i++)
            scanf("%d", &capacity[i]);
        if(hungary(n) == true)
            puts("YES");
        else
            puts("NO");
    }
    return 0;
}
posted @ 2015-02-13 16:23 JulyRina 閱讀(1451) | 評論 (0)編輯 收藏
題目大意:動物王國中有三類動物A,B,C,這三類動物的食物鏈構成了有趣的環形。A吃B, B吃C,C吃A。 
現有N個動物,以1-N編號。每個動物都是A,B,C中的一種,但是我們并不知道它到底是哪一種。 
有人用兩種說法對這N個動物所構成的食物鏈關系進行描述: 
第一種說法是"1 X Y",表示X和Y是同類。 
第二種說法是"2 X Y",表示X吃Y。 
此人對N個動物,用上述兩種說法,一句接一句地說出K句話,這K句話有的是真的,有的是假的。當一句話滿足下列三條之一時,這句話就是假話,否則就是真話。 
1) 當前的話與前面的某些真的話沖突,就是假話; 
2) 當前的話中X或Y比N大,就是假話; 
3) 當前的話表示X吃X,就是假話。 
你的任務是根據給定的N(1 <= N <= 50,000)和K句話(0 <= K <= 100,000),輸出假話的總數。 
題目分析;由于N和K很大,所以必須高效的維護動物之間的關系,并快速判斷是否殘生了矛盾,所以決定采用并查集。
對于每只動物i創建3個元素i,i+N,i+2N,并用這3*N個元素創建并查集。這個并查集維護如下信息:
i+xN表示“i屬于種類x”。
并查集里的每一個組表示組內所有元素代表的都同時發生或不發生。
同時,在合并之前先判斷合并是否會產生矛盾。
#include <cstdio>
#include <iostream>
using namespace std;

const int maxn = 50050;

int father[maxn*3], n, m, ans;

void init() {
    for(int i=0;i<3*n;i++) father[i] = i;
}
int find(int x) {
    return x == father[x] ? x : father[x] = find(father[x]);
}
void unite(int x, int y) {
    int a = find(x) , b = find(y);
    father[a] = father[b] = father[x] = father[y] = min(a, b);
}
bool check(int d, int x, int y) {
    if(x >= n || y >= n || x < 0 || y < 0)
        return false;
    if(d == 2 && x == y) 
        return false;
    if(d == 1) {
        if(find(x) == find(y+n) || find(x) == find(y+2*n))
            return false;
        unite(x, y);
        unite(x+n, y+n);
        unite(x+2*n, y+2*n);
        return true;
    } else {
        if(find(x) == find(y) || find(x) == find(y+2*n))
            return false;
        unite(x, y+n);
        unite(x+n, y+2*n);
        unite(x+2*n, y);
        return true;
    }
}
int main() {
    scanf("%d%d" , &n, &m);
    init();
    ans = 0;
    for(int i=0;i<m;i++) {
        int d, x, y;
        scanf("%d%d%d", &d, &x, &y);
        x --; y --;
        if(check(d, x, y) == false)
            ans ++;
    }
    printf("%d\n", ans);
    return 0;
}
posted @ 2015-02-11 17:33 JulyRina 閱讀(326) | 評論 (0)編輯 收藏
題目大意:給定長度為N的字符串S,要構造一個長度為N的字符串T。起初,T是一個空串,隨后反復進行下列任意操作。
從S的頭部刪除一個字符,加到T的尾部
從S的尾部刪除一個字符,加到T的尾部
目標是構造字典序盡可能小的字符串。
題目分析;從字典序的性質上看,無論T的末尾有多大,只要前面部分較小就可以。所以我們可以試一下如下貪心算法:
不斷取S和T的末尾中較小的一個字符放到T的末尾
這個算法已經接近正確了,只是針對S的開頭和末尾字符相同的情形還沒有定義。在這種情況下,因為我們希望能夠盡早使用更小的字符,所以就要比較一下下一個字符的大小。下一個字符也有可能相同,因此就有如下算法:
按照字典序比較字符串S和S反轉后的字符串S'。
如果S較小,就從S的開頭取出一個文字,放到T的末尾。
如果S'較小,就從S的末尾取出一個文字,放到T的末尾。
(若果相同則去哪一個都可以)
#include <cstdio>

char s[2002], tmp[2];
int n;

int main() {
    while(~scanf("%d", &n)) {
        for(int i=0;i<n;i++) {
            scanf("%s", tmp);
            s[i] = tmp[0];
        }
        int l = 0, r = n - 1, cnt = 0;
        while(l <= r) {
            int i = 0, left = true;
            while(l + i <= r - i) {
                if(s[l+i] < s[r-i]) {
                    break;
                }
                if(s[l+i] > s[r-i]) {
                    left = false;
                    break;
                }
                i ++;
            }
            if(left == true) putchar(s[l++]);
            else putchar(s[r--]);
            cnt ++;
            if(cnt % 80 == 0) putchar('\n');
        }
        if(cnt % 80) putchar('\n');
    }
    return 0;
}
posted @ 2015-02-11 15:59 JulyRina 閱讀(2255) | 評論 (0)編輯 收藏
題目大意:有一個大小為N*M的園子,雨后積起了水。八連通的積水被認為是連接在一起的。請求出園子里總共有多少水洼?
(八連通指下圖中相當于W的*)
***
*W*
***
題目分析;從任意的W開始,不同的用一個vis數組標記是否訪問過。一次DFS后與初始的這個W相連的W就都被標記過了。因此知道途中不再存在W為止,總共進行DFS的次數就是答案了。
#include <cstdio>
#include <cstring>

const int maxn = 101;

int n, m, ans;
char g[maxn][maxn];
bool vis[maxn][maxn];

void dfs(int x, int y) {
    if(vis[x][y] == true || g[x][y] != 'W') return;
    vis[x][y] = true;
    for(int i=-1;i<=1;i++)
    for(int j=-1;j<=1;j++) {
        if(!i && !j || x + i < 0 || x + i >= n || y + j < 0 || y + j >= m) continue;
        dfs(x+i, y+j);
    }
    return;
}

int main() {
    while(~scanf("%d%d" , &n, &m)) {
        for(int i=0;i<n;i++) {
            scanf("%s", g[i]);
            memset(vis+i, falsesizeof(bool)*(m));
        }
        ans = 0;
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
                if(g[i][j] == 'W' && !vis[i][j]) {
                    ans ++;
                    dfs(i, j);
                }
        printf("%d\n", ans);
    }
    return 0;
}
posted @ 2015-02-11 15:29 JulyRina 閱讀(557) | 評論 (0)編輯 收藏
僅列出標題
共3頁: 1 2 3 
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美大胆成人| 亚洲视频图片小说| 尹人成人综合网| 国产精品一香蕉国产线看观看| 欧美高清视频www夜色资源网| 鲁大师影院一区二区三区| 免费在线播放第一区高清av| 欧美xxxx在线观看| 欧美日韩精品免费看| 国产精品久久久久久久久借妻| 国产精品免费区二区三区观看| 国产精品永久入口久久久| 国产日韩欧美亚洲一区| 国产美女精品在线| 国产欧美日韩精品在线| 精品成人一区二区| 亚洲国产精品久久久久| 中文精品在线| 久久精品理论片| 欧美成人精品1314www| 亚洲第一福利视频| 久久婷婷久久| 免费永久网站黄欧美| 亚洲国产精品va在看黑人| 99热这里只有成人精品国产| 亚洲一区中文字幕在线观看| 一本一本久久a久久精品综合麻豆| 欧美大片免费观看在线观看网站推荐| 欧美激情一区二区三区在线视频观看 | 欧美激情偷拍| 国产毛片久久| 日韩网站免费观看| 久久这里有精品15一区二区三区| 亚洲国产日韩欧美在线动漫| 亚洲欧美日韩一区二区三区在线| 看欧美日韩国产| 国产精品久久久久久久久免费樱桃| 国产一区美女| 亚洲一区二区三区精品在线| 久久综合伊人77777| 亚洲裸体俱乐部裸体舞表演av| 欧美一区二区三区久久精品茉莉花 | 亚洲一二三级电影| 欧美粗暴jizz性欧美20| 国产精品中文在线| 国内精品美女在线观看| 一区二区三区免费网站| 亚洲国产成人av| 久久久久www| 国产欧美日韩一区二区三区在线| 亚洲国产三级在线| 亚洲午夜精品一区二区三区他趣| 欧美激情第三页| 久久一二三国产| 精品成人a区在线观看| 久久久国产午夜精品| 亚洲一区二区三区在线| 欧美性jizz18性欧美| 亚洲日本欧美在线| 男女激情久久| 母乳一区在线观看| 亚洲精品久久久久| 亚洲国产精品福利| 免费在线日韩av| 亚洲精品一区久久久久久| 免费观看欧美在线视频的网站| 久久久久国产精品厨房| 亚洲国产专区校园欧美| 亚洲国产日韩一区二区| 欧美人在线观看| 亚洲一区二区在线播放| 中国亚洲黄色| 国产精品日韩在线观看| 香蕉乱码成人久久天堂爱免费 | 黄色国产精品一区二区三区| 久久免费视频观看| 久久一区二区三区四区五区| 亚洲精品极品| 亚洲永久免费av| 日韩午夜av| 国产精品一香蕉国产线看观看 | 一区二区三区欧美成人| 一道本一区二区| 国产精品久久午夜夜伦鲁鲁| 羞羞答答国产精品www一本| 午夜在线视频观看日韩17c| 国产欧美一区二区三区久久| 久久阴道视频| 欧美国产精品中文字幕| 亚洲天堂免费观看| 久久高清福利视频| 亚洲精品视频在线播放| 亚洲欧美成人精品| 亚洲国产精品专区久久| 中文av字幕一区| 一区视频在线看| 国产三级精品三级| 欧美一级网站| 欧美一级专区| 亚洲精品视频免费在线观看| 亚洲宅男天堂在线观看无病毒| 国模私拍视频一区| 99re热精品| 国产自产高清不卡| 亚洲精品网站在线播放gif| 国产视频自拍一区| 亚洲国产精品一区| 91久久在线播放| 亚洲女人天堂av| 免费观看亚洲视频大全| 亚洲欧美日韩精品久久奇米色影视| 久久久久久久久综合| 亚洲欧美99| 欧美激情一二区| 久久亚洲精品欧美| 国产欧美精品xxxx另类| 日韩一级在线观看| 亚洲欧洲美洲综合色网| 久久国产精品久久久久久| 一本不卡影院| 鲁大师成人一区二区三区 | 一区二区三区高清在线观看| 久久色在线观看| 久久成人资源| 国产精品久久久久国产a级| 亚洲经典一区| 亚洲国产精品成人综合色在线婷婷| 亚洲自拍偷拍麻豆| 午夜精品久久久久影视| 欧美日韩一区二区视频在线观看| 欧美激情一区| 亚洲国产一区在线| 久久亚洲捆绑美女| 久久亚洲免费| 国产亚洲一区二区在线观看| 亚洲在线观看视频| 亚洲一区精品视频| 欧美视频一区| 国产精品久久久久久久久久ktv| 国产人成精品一区二区三| 99爱精品视频| 一区二区欧美日韩视频| 欧美日韩精品中文字幕| 夜夜嗨av一区二区三区四季av| 亚洲免费久久| 欧美日韩亚洲一区在线观看| 亚洲免费观看高清完整版在线观看熊| 亚洲国产精品va| 欧美激情综合网| 日韩午夜在线电影| 午夜精品亚洲一区二区三区嫩草| 国产精品女同互慰在线看| 亚洲视屏一区| 久久人人爽爽爽人久久久| 在线精品视频在线观看高清| 欧美国产欧美综合 | 性欧美videos另类喷潮| 国产视频精品va久久久久久| 欧美中在线观看| 国产精品网站视频| 亚洲视频一起| 亚洲视频成人| 国产日韩精品一区| 久久久成人网| 亚洲国产成人久久综合| 日韩亚洲成人av在线| 欧美午夜激情小视频| 午夜免费久久久久| 欧美成人午夜| 亚洲午夜性刺激影院| 国产日韩欧美黄色| 欧美激情第8页| 美女视频一区免费观看| 亚洲精品国产欧美| 国产精品福利片| 久久人人爽国产| 9l国产精品久久久久麻豆| 久久精品99国产精品日本| 亚洲国产精品综合| 国产精品婷婷| 欧美国产日本韩| 午夜精品久久久久影视| 亚洲激情在线观看| 看欧美日韩国产| 亚洲免费一在线| 亚洲激情第一区| 国产亚洲激情视频在线| 欧美日韩在线视频一区| 久久精品中文字幕免费mv| 亚洲精品国产精品国自产观看| 欧美在线视频在线播放完整版免费观看 | 亚洲第一精品久久忘忧草社区| 亚洲自拍偷拍麻豆| 亚洲国产成人在线| 国产模特精品视频久久久久| 欧美风情在线| 性久久久久久久| 一区二区三区日韩精品视频| 欧美大尺度在线观看| 久久阴道视频|