保鏢(hall定理&&集合動規&&優化)
【問題描述】
蒟蒻YxuanwKeith想成為Philisweng的保鏢,但是作為預備隊員的保鏢智商肯定也不能低,至少要回答出下面這個問題:現在有一副若干條邊的二分圖,左邊有N個點ai,右邊有M個點bi,每個點都有一個權值wi。一個合法的子圖滿足以下兩個限制:
1.選出的點權和大於等於限制t。
2.並且可以從圖中選出若干條邊,使得二分圖中每個點最多被一條邊覆蓋,而選出的點要恰好被一條邊覆蓋。
求總方案數。由於YxuanwKeith很弱,所以他找到你來回答這個問題。
【輸入格式】
第一行包括兩個數N,M,分別表示左邊點的個數和右邊點的個數。
第2行到第N+1行,第i行一個長度為M的字串Si,第j個字元如果是0表示左邊第i-1個點和右邊第j個點沒有連邊,如果是1表示有連邊。
第N+2行,N個非負整數,第i個非負整數表示左邊第i個點的權值Wi
第N+3行,M個非負整數,第i個非負整數表示右邊第i個點的權值vi
第N+4行,一個正整數t,表示題目中的限制。
【輸出格式】
輸出共一行,一個數,表示答案。
【輸入樣例】
3 3
010
111
010
1 2 3
8 5 13
21
【輸出樣例】
3
【樣例解釋】
樣例中的二分圖如圖所示:
子集{a1,a2,b2,b3}可以通過選擇邊集{(a1,b2),(a2,b3)}滿足條件二,並且權值和是21。
子集{a3,b2,b3}和{a2,a3,b2,b3}都可以通過選擇邊集{(a2,b2),(a3,b2)}滿足兩個條件。
其餘方案都不合法,所以答案是3。
【資料範圍】
對於30%的資料:N,M<=8
對於另外30%的資料:左邊的點和右邊的點兩兩間都有連邊。
對於100%的資料:N,M<=20,t<=4*10^8,wi,vi<=10^8
【來源】
一道很神奇的題,開始用的網路流,結果全錯。
正解是集合動規,主要是要用hall定理(請自行百度)
根據hall定理我們可以用動規的思想來檢視一個集合是否成立,然後把權值和記錄下來,這樣我們就可以得到2個陣列。(不成立的權值應該是負無窮)
然後一個排序,在另一個上進行二分查詢就可以了。
一些必要的優化請自行檢視程式碼
完整程式碼如下:
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=45;
const ll inf=20000000000000ll;
vector<int>g[maxn];
ll d[1<<21]={0},q[1<<21]={0};
int m,n,a[maxn];
bool vis[21];
ll b[maxn];
ll t;
char s[maxn];
ll work(int x)
{
int num1=0,num2=0;
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++) if((a[i]&x)==a[i])
{
num1++;
for(int k=0;k<g[i].size();k++) if(!vis[g[i][k]])
vis[g[i][k]]=1,num2++;
if(num2>=n-i+num1) return 1;
}
return num1<=num2;
}
ll work2(int x)
{
int num1=0,num2=0;
memset(vis,0,sizeof(vis));
for(int i=1;i<=m;i++) if((a[i]&x)==a[i])
{
num1++;
for(int k=0;k<g[i+n].size();k++) if(!vis[g[i+n][k]])
vis[g[i+n][k]]=1,num2++;
if(num2>=m-i+num1) return 1;
}
return num1<=num2;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",s);
for(int k=0;k<m;k++) if(s[k]=='1')
{
g[i].push_back(k+1);
g[k+1+n].push_back(i);
}
}
for(int i=1;i<=n+m;i++) cin>>b[i];
cin>>t;
a[1]=1;
for(int i=2;i<=max(n,m);i++)a[i]=a[i-1]*2;
int k1=1<<n,k2=1<<m;
for(int i=1;i<k1;i++)
{
if(d[i]!=-inf&&!work(i)) //根據hall定理進行判斷
for(int j=i;j<k1;j++) if((i&j)==i)
d[j]=-inf;
}
for(int i=1;i<k2;i++)
{
if(q[i]!=-inf&&!work2(i))
for(int j=i;j<k2;j++) if((i&j)==i)
q[j]=-inf;
}
for(int i=1;i<k1;i++) if(d[i]!=-inf)
for(int j=1;j<=n;j++) if((a[j]&i)==a[j])//算權值和的時候的優化
{
d[i]=b[j]+d[i-a[j]];
break;
}
for(int i=1;i<k2;i++) if(q[i]!=-inf)
for(int j=1;j<=m;j++) if((a[j]&i)==a[j])
{
q[i]=b[j+n]+q[i-a[j]];
break;
}
sort(d,d+k1);
ll ans=0;
for(int i=0;i<k2;i++) if(q[i]!=-inf)//二分找答案
{
ans+=k1-(lower_bound(d,d+k1,t-q[i])-d);
}
cout<<ans;
return 0;
}
相關文章
- Hall定理總結
- [Lucas定理] 集合計數
- 斜率優化動態規劃優化動態規劃
- Java效能優化技巧集合Java優化
- 5個規則,確保你的微服務優化執行微服務優化
- 戀愛腦?No,愛情保鏢還得靠AI!AI
- 集合上的動態規劃動態規劃
- 運籌優化(七)--動態規劃解析優化動態規劃
- 介紹下,這就是綠盟科技“資料安全保鏢”
- 01揹包動態規劃空間優化動態規劃優化
- SOAR的IDE外掛——您的貼身DBA保鏢IDE
- 多重揹包動態規劃及空間優化動態規劃優化
- 有關動態規劃的相關優化思想動態規劃優化
- MySQL 優化三(優化規則)(高階篇)MySql優化
- 運籌優化(十三)--大規模優化方法優化
- Electron 截圖踩坑和優化集合優化
- 對QQ保鏢檢測結果的幾點疑問薦
- MySQL 規範及優化MySql優化
- 基於數值優化的自動駕駛實時運動規劃優化自動駕駛
- Java集合類操作優化經驗總結Java優化
- Android優化UI規則Android優化UI
- Android 效能優化 ---- 啟動優化Android優化
- 【演算法】動態規劃-優化編輯器問題演算法動態規劃優化
- 企業網站的全能保鏢——華為雲網站安全解決方案網站
- 網路安全才是人工智慧產業平穩發展的“保鏢”人工智慧產業
- 等保合規的首選:雷池 WAF 具有哪些優勢?
- selenium自動化測試面試集合面試
- 【java】【集合】set集合、唯一性保證、Linkset、案例Java
- iOS啟動優化iOS優化
- App啟動優化APP優化
- iOS效能優化 - APP啟動時間優化iOS優化APP
- Flutter程式碼規範優化記錄Flutter優化
- 正規表示式效能優化的探究優化
- Linux系統安全常規優化Linux優化
- Android效能優化筆記(一)——啟動優化Android優化筆記
- Android應用優化之冷啟動優化Android優化
- 面試Tip:Android優化之APP啟動優化面試Android優化APP
- EntityFramework優化:第一次啟動優化Framework優化