首页 > 代码库 > 20170612测试
20170612测试
问题 A: 装果子
时间限制: 1 Sec 内存限制: 128 MB提交: 96 解决: 54
题目描述
果园里有n颗果树,每棵果树都有一个编号i(1≤i≤n)。小明已经把每棵果树上的果子都摘下来堆在了这棵树的下方,每棵树下方的果子体积为ai。
现在小明将拿来m个袋子把这些果子都装进袋子里。每个袋子的体积为v。小明会按照如下规则把果子装进袋子里:
(a)从第1棵果树开始装起,由1到n一直装到第n棵果树。
(b)如果这棵果树下的果子能全部装进当前这个袋子,就装进去;如果不能,就关上当前这个袋子,打开一个新的袋子开始装。
小明希望在能把所有果子都装进袋子里的前提下,v尽量小。m个袋子并不一定都要装进果子。
输入
输入文件名为fruit.in
输入第1行,包含两个整数n和m。
第2行,包含n个整数ai。
输出
输出文件名为fruit.out
输出仅1行,表示最小的v。
样例输入
样例输出
提示
【数据范围】
对于40%的数据,0<m≤n≤1,000,0<ai≤1,000;
对于70%的数据,0<m≤n≤100,000,0<ai≤100,000;
对于100%的数据,0<m≤n≤100,000,0<ai≤1,000,000,000。
裸裸的二分:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #define ll long long 5 using namespace std; 6 int a[100005]; 7 int n,m; 8 bool check(ll x){ 9 ll v=x; int nu=1; 10 for (int i=1;i<=n;i++) 11 if (v>=(ll)a[i]) v-=(ll)a[i]; 12 else v=x-(ll)a[i],nu++; 13 if (nu<=m) return 1; 14 return 0; 15 } 16 int main(){ 17 ll sum=0,Max=0; 18 scanf("%d%d",&n,&m); 19 for (int i=1;i<=n;i++){ 20 scanf("%d",&a[i]); 21 sum+=(ll)a[i]; 22 Max=max(Max,(ll)a[i]); 23 } 24 ll l=Max,r=sum; 25 while (l<r){ 26 ll mid=(l+r)>>1; 27 if (check(mid)) r=mid; 28 else l=mid+1; 29 } 30 printf("%lld",l); 31 return 0; 32 }
问题 B: 零件加工
时间限制: 1 Sec 内存限制: 128 MB提交: 107 解决: 46
题目描述
工匠小K最近有n个零件需要加工。每个零件都需要ti天的时间来完成,每个零件每延迟一天加工都要缴纳一定的罚金si。延迟的天数为从今天算起到该工作开始的那天,第一个零件加工没有罚金。现在小K想知道怎样安排加工顺序可以使他要交的罚金最少,最少是多少。
这个数可能会很大,请输出这个数对m取模后的结果。
输入
输入文件名为process.in。
输入第一行为一个整数n,表示需要加工的零件总数。
第二行为一个整数m,表示答案要对m取模。
第3~n+2行,每行两个整数ti和si。
输出
输出文件名为process.out。
输出仅一行,一个整数,表示小K最少要缴纳的罚金对m取模的结果。
样例输入
样例输出
提示
贪心,按照t/s升序排序然后计算。
解题关键是以状态下相邻两个的选择。
详细证明见HK。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 #include<cmath> 6 #include<cstdlib> 7 #include<vector> 8 using namespace std; 9 typedef long long ll; 10 typedef long double ld; 11 typedef pair<int,int> pr; 12 const double pi=acos(-1); 13 #define rep(i,a,n) for(int i=a;i<=n;i++) 14 #define per(i,n,a) for(int i=n;i>=a;i--) 15 #define Rep(i,u) for(int i=head[u];i;i=Next[i]) 16 #define clr(a) memset(a,0,sizeof(a)) 17 #define pb push_back 18 #define mp make_pair 19 #define fi first 20 #define sc second 21 #define pq priority_queue 22 #define pqb priority_queue <int, vector<int>, less<int> > 23 #define pqs priority_queue <int, vector<int>, greater<int> > 24 #define vec vector 25 ld eps=1e-9; 26 ll pp=1000000007; 27 ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;} 28 ll mul(ll a,ll b,ll pp){ll ans=0;for(;b;b>>=1,a=mo(a+a,pp))if(b&1)ans=mo(ans+a,pp);return ans;} 29 void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); } 30 //void add(int x,int y,int z){ v[++e]=y; next[e]=head[x]; head[x]=e; cost[e]=z; } 31 int dx[5]={0,-1,1,0,0},dy[5]={0,0,0,-1,1}; 32 ll read(){ ll ans=0; char last=‘ ‘,ch=getchar(); 33 while(ch<‘0‘ || ch>‘9‘)last=ch,ch=getchar(); 34 while(ch>=‘0‘ && ch<=‘9‘)ans=ans*10+ch-‘0‘,ch=getchar(); 35 if(last==‘-‘)ans=-ans; return ans; 36 } 37 #define N 100005 38 struct node{ 39 ll t,s; 40 }f[N]; 41 bool cmp(node a,node b){ 42 return b.s*a.t<a.s*b.t; 43 } 44 int main() 45 { 46 int n=read(); ll m=read(),sum=0,ans=0; 47 for (int i=1;i<=n;i++) f[i].t=read(),f[i].s=read(); 48 sort(f+1,f+n+1,cmp); 49 for (int i=1;i<=n;i++){ 50 ans=mo(ans+mul(f[i].s,sum,m),m); 51 sum=mo(sum+f[i].t,m); 52 } 53 printf("%lld",mo(ans,m)); 54 return 0; 55 }
问题 C: 种树
时间限制: 2 Sec 内存限制: 128 MB题目描述
为了绿化乡村,H村积极响应号召,开始种树了。
H村里有n幢房屋,这些屋子的排列顺序很有特点,在一条直线上。于是方便起见,我们给它们标上1~n。树就种在房子前面的空地上。
同时,村民们向村长提出了m个意见,每个意见都是按如下格式:希望第li个房子到第ri个房子的房前至少有ci棵树。
因为每个房屋前的空地面积有限,所以每个房屋前最多只能种ki棵树。
村长希望在满足村民全部要求的同时,种最少的树以节约资金。请你帮助村长。
输入
输入文件名为tree.in
输入第1行,包含两个整数n,m。
第2行,有n个整数ki。
第2~m+1行,每行三个整数li,ri,ci。
输出
输出文件名为tree.out
输出1个整数表示在满足村民全部要求的情况下最少要种的树。村民提的要求是可以全部满足的。
样例输入
样例输出
提示
【数据范围】
对于30%的数据,0<n≤100,0<m≤100,ki=1;
对于50%的数据,0<n≤2,000,0<m≤5,000,0<ki≤100;
对于70%的数据,0<n≤50,000,0<m≤100,000,0<ki≤1,000;
对于100%的数据,0<n≤500,000,0<m≤500,000,0<ki≤5,000
法一差分约束:
开一个s数组记录前缀和。
根据题意我们可以得到3个约束条件:
s[r]-s[l-1]≥c,①
s[i]≥s[i-1],②
s[i]-s[i-1]≤k,③
根据①得s[l]-s[r]≤-c,在r和l-1之间连一条权值为-c的边。
根据②得s[i-1]-s[i]≤0,在i和i-1之间连一条权值为0的边。
根据③在i-1和i之间连一条权值为k的边。
50分算法:Bellman-Ford。
时间复杂度:O(nm)
70分算法:SPFA。
时间复杂度:O(km)
100分算法:
观察到最大可能需要连150w条边,因此我们要考虑有些边是否需要连。
我们可以只根据条件①计算,每次更新后O(n)检查是否满足条件②和③,如果不满足就修改,这样只用连50w条边,可以过全部数据。
Tip:事实上SPFA可以过100%
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 #include<cmath> 6 #include<cstdlib> 7 #include<vector> 8 using namespace std; 9 typedef long long ll; 10 typedef long double ld; 11 typedef pair<ll,ll> pr; 12 const double pi=acos(-1); 13 #define rep(i,a,n) for(int i=a;i<=n;i++) 14 #define per(i,n,a) for(int i=n;i>=a;i--) 15 #define Rep(i,u) for(int i=head[u];i;i=Next[i]) 16 #define clr(a) memset(a,0,sizeof(a)) 17 #define pb push_back 18 #define mp make_pair 19 #define fi first 20 #define sc second 21 #define pq priority_queue 22 #define pqb priority_queue <int, vector<int>, less<int> > 23 #define pqs priority_queue <int, vector<int>, greater<int> > 24 ld eps=1e-9; 25 ll pp=1000000007; 26 ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;} 27 ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;} 28 void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); } 29 //void add(int x,int y,int z){ v[++e]=y; next[e]=head[x]; head[x]=e; cost[e]=z; } 30 int dx[5]={0,-1,1,0,0},dy[5]={0,0,0,-1,1}; 31 ll read(){ ll ans=0; char last=‘ ‘,ch=getchar(); 32 while(ch<‘0‘ || ch>‘9‘)last=ch,ch=getchar(); 33 while(ch>=‘0‘ && ch<=‘9‘)ans=ans*10+ch-‘0‘,ch=getchar(); 34 if(last==‘-‘)ans=-ans; return ans; 35 } 36 #define N 500005 37 #include<queue> 38 const ll inf=1e15; 39 vector<pr> vec[N]; 40 ll dis[N]; 41 bool v[N]; 42 void spfa(int x){ 43 for (int i=0;i<N;i++) dis[i]=inf,v[i]=0; 44 dis[x]=0; v[x]=1; 45 queue<int> q; q.push(x); 46 while (!q.empty()){ 47 int u=q.front(); q.pop(); v[u]=0; 48 for (int i=0;i<vec[u].size();i++){ 49 int v_=vec[u][i].fi,c=vec[u][i].sc; 50 if (dis[v_]>dis[u]+c){ 51 dis[v_]=dis[u]+c; 52 if (!v[v_]) q.push(v_),v[v_]=1; 53 } 54 } 55 } 56 } 57 int main(){ 58 int n=read(),m=read(); 59 for (int i=1;i<=n;i++){ 60 int k=read(); 61 vec[i-1].pb(mp(i,k)); 62 vec[i].pb(mp(i-1,0)); 63 } 64 for (int i=1;i<=m;i++){ 65 int a=read(),b=read(),c=read(); 66 vec[b].pb(mp(a-1,-c)); 67 } 68 spfa(n); 69 printf("%lld",-dis[0]); 70 return 0; 71 }
法二贪心:
题目中要求要种树种得少,就要使一棵树给多个区间使用,这样,尽量在重叠区间种树即可,而重叠位置一定是区间尾部。处理问题时,先按所有区间的结束位置排序,若结束位置相同,则按开始位置从大到小排序。之后依次处理每个区间,先在第一个区间尾部种满足要求的树,对下一个区间,看差多少棵就在该区间尾部种多少。
【算法步骤】:
1.先快排
2.对每个区间依次处理
a.从前到后扫描这个区间,统计点的个数;
b.若点的个数超过了要求的点数,则continue;
c.从该区间后向前扫描,添加覆盖点。
3.输出ans
Tip:这种方法只适用于小数据,对于大数据我们可以用树状数组加快求和,即过程a,还可以用并查集的方法来加速种树过程c,当有一段区间的树种满时把它与前一个区间合并就行了。
还是考虑一下 (FROM Lhq)
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 #include<cmath> 6 #include<cstdlib> 7 #include<vector> 8 using namespace std; 9 typedef long long ll; 10 typedef long double ld; 11 typedef pair<int,int> pr; 12 const double pi=acos(-1); 13 #define rep(i,a,n) for(int i=a;i<=n;i++) 14 #define per(i,n,a) for(int i=n;i>=a;i--) 15 #define Rep(i,u) for(int i=head[u];i;i=Next[i]) 16 #define clr(a) memset(a,0,sizeof(a)) 17 #define pb push_back 18 #define mp make_pair 19 #define fi first 20 #define sc second 21 #define pq priority_queue 22 #define pqb priority_queue <int, vector<int>, less<int> > 23 #define pqs priority_queue <int, vector<int>, greater<int> > 24 #define vec vector 25 ld eps=1e-9; 26 ll pp=1000000007; 27 ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;} 28 ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;} 29 void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); } 30 //void add(int x,int y,int z){ v[++e]=y; next[e]=head[x]; head[x]=e; cost[e]=z; } 31 int dx[5]={0,-1,1,0,0},dy[5]={0,0,0,-1,1}; 32 ll read(){ ll ans=0; char last=‘ ‘,ch=getchar(); 33 while(ch<‘0‘ || ch>‘9‘)last=ch,ch=getchar(); 34 while(ch>=‘0‘ && ch<=‘9‘)ans=ans*10+ch-‘0‘,ch=getchar(); 35 if(last==‘-‘)ans=-ans; return ans; 36 } 37 #define N 500000 38 #define lowbit(i) (i&(-i)) 39 struct node{ 40 int a,b; ll v; 41 }f[N+5]; 42 ll T[N+5],k[N+5],fl[N+5]; ll ans; 43 void add(int a,int b){ 44 for (int i=a;i<=N;i+=lowbit(i)) T[i]+=b; 45 } 46 ll sum(int a){ 47 ll Sum=0; 48 for (int i=a;i>0;i-=lowbit(i)) Sum+=T[i]; 49 return Sum; 50 } 51 bool cmp(node a,node b){ 52 return a.b<b.b||a.b==b.b&&a.a<a.b; 53 } 54 int main(){ 55 int n=read(),m=read(); 56 for (int i=1;i<=n;i++) k[i]=read(); 57 for (int i=1;i<=m;i++) { 58 f[i].a=read(); f[i].b=read(); f[i].v=read(); 59 } 60 sort(f+1,f+m+1,cmp); 61 for (int i=1;i<=m;i++){ 62 ll Sum=sum(f[i].b)-sum(f[i].a-1); 63 if (f[i].v>Sum){ 64 int j=f[i].b; 65 while (Sum<f[i].v){ 66 if (fl[j]>0) j=fl[j]; 67 if (f[i].v>=Sum+k[j]){ 68 Sum+=k[j]; ans+=k[j]; add(j,k[j]); k[j]=0; j--; 69 } else { 70 ans+=f[i].v-Sum; add(j,f[i].v-Sum); k[j]-=f[i].v-Sum; Sum=f[i].v; 71 } 72 } 73 fl[f[i].b]=j; 74 } 75 //cout<<i<<" "<<ans<<endl; 76 } 77 printf("%lld",ans); 78 return 0; 79 }
问题 D: 完全平方数
时间限制: 1 Sec 内存限制: 128 MB提交: 74 解决: 36
题目描述
一个数如果是另一个整数的完全平方,那么我们就称这个数为完全平方数(Pefect Sqaure),也称平方数。
小A认为所有的平方数都是很perfect的~
于是他给了小B一个任务:用任意个不大于n的不同的正整数相乘得到完全平方数,并且小A希望这个平方数越大越好。
请你帮助小B告诉小A满足题意的最大的完全平方数。
输入
输入文件名为number.in
输入仅 1行,一个数n。
输出
输出文件名为number.out
输出仅1行,一个数表示答案。由于答案可以很大,所以请输出答案对100000007取模后的结果。
样例输入
样例输出
提示
【数据范围】
对于20%的数据,0<n≤100;
对于50%的数据,0<n≤5,000;
对于70%的数据,0<n≤100,000;
对于100%的数据,0<n≤5,000,000。
数的阶乘中所有质因数的幂数整除2*2;
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 const int pp=100000007; 6 #define ll long long 7 long long Ans=1LL; 8 #define N 5000005 9 int pr[N],f[N]; 10 ll q(ll a,ll b){ 11 ll ans=1LL; for (;b;b>>=1,a=(a*a)%pp) if (b&1) ans=(ans*a)%pp; return ans; 12 } 13 int main(){ 14 int n,nu=0; scanf("%d",&n); 15 for (int i=2;i<=n;i++){ 16 if (!f[i]) pr[++nu]=i; 17 for (int j=1;j<=nu && pr[j]*i<=n;j++){ 18 f[pr[j]*i]=1; 19 if (i%pr[j]==0) break; 20 } 21 } 22 for (int i=1;i<=nu;i++){ 23 ll x=(ll)pr[i],ans=0LL; 24 while (x<=(ll)n){ 25 ans+=((ll)n/x); 26 x*=(ll)pr[i]; 27 } 28 Ans=(Ans*q((ll)pr[i],ans>>1))%pp; 29 } 30 printf("%lld",q(Ans,2)); 31 return 0; 32 }
问题 E: 卡片游戏
时间限制: 1 Sec 内存限制: 128 MB提交: 50 解决: 21
题目描述
小D举办了元旦联欢活动,其中有一个卡片游戏。
游戏的规则是这样的:有n张卡片,每张卡片上正面写着一个小于等于100的正整数ai,反面都是一样的花色。这n张卡片正面朝下叠成一堆,玩这个游戏的人从中可以抽出连续的k(1≤k≤n)张卡片。如果对于这k张卡片上的数字的平均值a,满足l<=a<=r,那他就可以获得小礼物一件。
小W来玩这个游戏了,她事先通过某些途径知道了这n张卡片上写的数字,现在她想知道她获得小礼物的期望值。
小W对小数很头疼,所以请你用分数的形式告诉她答案。
输入
输入文件名为game.in
输入第1行,三个整数n,l,r。
第2行,包含n个整数ai。
输出
输出文件名为game.out
输出仅1行,表示小W获得小礼物的期望值。输出格式为“P/Q”(P和Q互质)。如果期望值是0或1就不用输出分数了
样例输入
样例输出
提示
【输入输出样例解释1】
【输入输出样例解释1】抽出的卡片 |
a(保留2位小数) |
是否满足l<=a<=r |
|
3 |
3.00 |
√ |
|
1 |
1.00 |
||
2 |
2.00 |
√ |
|
4 |
4.00 |
||
3,1 |
2.00 |
√ |
|
1,2 |
1.50 |
||
2,4 |
3.00 |
√ |
|
3,1,2 |
2.00 |
√ |
|
1,2,4 |
2.33 |
√ |
|
3,1,2,4 |
2.50 |
√ |
|
【输入输出样例解释2】
由上表得,小W总是可以获得小礼物。因此期望值是1
【数据范围】
对于30%的数据,0<n≤10,000;
对于70%的数据,0<n≤100,000;
对于100%的数据,0<n≤500,000,0<l<r≤100。
由表可得,一共有10种情况,其中有7种情况小W可以获得小礼物。因此小W获得小礼物的期望值是7/10。
数学推:
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 #include<cmath> 6 #include<cstdlib> 7 #include<vector> 8 using namespace std; 9 typedef long long ll; 10 typedef long double ld; 11 typedef pair<int,int> pr; 12 const double pi=acos(-1); 13 #define rep(i,a,n) for(int i=a;i<=n;i++) 14 #define per(i,n,a) for(int i=n;i>=a;i--) 15 #define Rep(i,u) for(int i=head[u];i;i=Next[i]) 16 #define clr(a) memset(a,0,sizeof(a)) 17 #define pb push_back 18 #define mp make_pair 19 #define fi first 20 #define sc second 21 #define pq priority_queue 22 #define pqb priority_queue <int, vector<int>, less<int> > 23 #define pqs priority_queue <int, vector<int>, greater<int> > 24 #define vec vector 25 ld eps=1e-9; 26 ll pp=1000000007; 27 ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;} 28 ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;} 29 void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); } 30 //void add(int x,int y,int z){ v[++e]=y; next[e]=head[x]; head[x]=e; cost[e]=z; } 31 int dx[5]={0,-1,1,0,0},dy[5]={0,0,0,-1,1}; 32 ll read(){ ll ans=0; char last=‘ ‘,ch=getchar(); 33 while(ch<‘0‘ || ch>‘9‘)last=ch,ch=getchar(); 34 while(ch>=‘0‘ && ch<=‘9‘)ans=ans*10+ch-‘0‘,ch=getchar(); 35 if(last==‘-‘)ans=-ans; return ans; 36 } 37 #define N 500005 38 ll gcd(ll a,ll b){ 39 return (b==0)?a:gcd(b,a%b); 40 } 41 ll cnta,cntb; 42 int a[N],b[N],c[N],sum[N]; 43 void mega(int l,int r){ 44 int mid=(l+r)>>1,i=l,j=mid+1,k=0; 45 while (i<=mid && j<=r){ 46 while (a[i]<a[j] && i<=mid){ 47 c[++k]=a[i]; i++; 48 } 49 while(a[i]>=a[j] && j<=r){ 50 c[++k]=a[j]; j++; cnta+=(ll)(mid-i+1); 51 } 52 } 53 while (i<=mid) c[++k]=a[i],i++; 54 while (j<=r) c[++k]=a[j],j++; 55 for (int i=1;i<=k;i++) 56 a[i+l-1]=c[i]; 57 } 58 void megb(int l,int r){ 59 int mid=(l+r)>>1,i=l,j=mid+1,k=0; 60 while (i<=mid && j<=r){ 61 while (b[i]<=b[j] && i<=mid){ 62 c[++k]=b[i]; i++; 63 } 64 while(b[i]>b[j] && j<=r){ 65 c[++k]=b[j]; j++; cntb+=(ll)(mid-i+1); 66 } 67 } 68 while (i<=mid) c[++k]=b[i],i++; 69 while (j<=r) c[++k]=b[j],j++; 70 for (int i=1;i<=k;i++) 71 b[i+l-1]=c[i]; 72 } 73 void msort(int l,int r){ 74 if (l==r) return; 75 int mid=(l+r)>>1; 76 msort(l,mid); 77 msort(mid+1,r); 78 mega(l,r); 79 megb(l,r); 80 } 81 int main(){ 82 int n=read(),l=read(),r=read(); 83 for (int i=1;i<=n;i++) { 84 int a_=read(); 85 sum[i]=sum[i-1]+a_; 86 a[i]=l*i-sum[i]; 87 b[i]=r*i-sum[i]; 88 } 89 msort(0,n); 90 ll B=(ll)n*((ll)n+1LL)/2LL; 91 ll A=cnta-cntb; 92 ll C=gcd(A,B); 93 if (A==0) printf("0"); 94 else if (A==B) printf("1"); 95 else printf("%lld/%lld",A/C,B/C); 96 return 0; 97 }
问题 F: 围栏问题
时间限制: 2 Sec 内存限制: 128 MB提交: 43 解决: 23
题目描述
在一片草原上,有n只兔子无忧无虑地生活着。这片草原可以划分成m×m的方阵。每个方格内最多有一只兔子。
一位饲养员负责喂养这些兔子。为了方便,她需要用篱笆建造最多k座围栏,将草原上的兔子全部围起来。
围栏需要满足以下条件:
(1)必须沿着网格线建造;
(2)每座围栏是一个不与自身重叠或相交的封闭回路;
(3)各座围栏之间互相不重叠、不相交;
(4)一座围栏不能被围在另一座围栏里面。
请你帮助饲养员计算一下围栏总长度的最小值。
输入
输入文件名为fence.in
输入第1行为三个整数m,k,n。
接下来n行每行为一对正整数x,y,表示第x行第y列的方格中有一只兔子。
输出
输出文件名为fence.out
输出仅1行,为一个正整数,表示围栏总长度的最小值。
样例输入
样例输出
提示
【数据范围】
对于10%的数据,k=1;
对于10%~30%的数据,k=2;
对于30%~60%的数据,n≤10;
对于100%的数据,1≤k≤n≤16,m≤1,000。
首先,两个围栏互相包含的情况不可能在最优解中出现(把里面那个拆掉可以节省篱笆,得到一个更优解)。
其次,两个围栏相交的情况也一定不是最优,如下图所示,把重叠的部分拆掉可以得到更优解。
由于我们的目标是最小化周长,可以得到这样一条重要性质:矩形的围栏比不规则形状的更优。如图,把里面的边往外平移,成为一个矩形的边框,这样可以在周长不变的情况下,围住更多的土地。(不用担心扩展出去的部分会和另外的围栏相交,因为这不可能在最优解中出现,刚才已经提到)
有些情况下,矩形边框不仅扩大围住的范围而且能节省周长。
所以,接下来我们只需考虑用矩形去包围兔子就够了。
定义基本矩形或叫极小矩形——假如这个矩形再往里缩小一点就会有兔子从里面逃出。
如就不是一个极小矩形,它可以往里收缩
现在它是一个极小矩形。
容易发现,最优解中必然只包含极小矩形。
解法一:
搜索+剪枝,枚举每一个兔子所在的栅栏,如果还可以再多一种栅栏就可以多一种选择:即开一个刚刚包围自己的栅栏。另一种选择就是和之前的某一个集合的兔子合并放到一个栅栏里,计算周长时找到上下左右的最大差值,记得+1。
解法二:
我们可以预处理出所有极小矩形:枚举所有兔子的非空子集,找出这个子集中最上、最左、最右、最下的兔子,就可得到一个极小矩形。(当然这样计算会出现许多重复的,剔除即可)。这种做法虽然是指数级的但是编码简单,而且n很小,仍可接受。
现在我们有了一堆矩形,对于每个矩形我们掌握它的两个属性:周长、围住了的兔子集合。问题转化为:从这堆矩形中选出不超过k个,在满足这些兔子集合互不相交,且其并集恰好是兔子全集的条件下,最小化总周长。
这是一个典型的精确覆盖问题,搜索解决即可。可以使用二进制来表示各个集合,用位运算的方法达到优化的效果。也可以用DLX来做,更快,更爽。
只有解法一,法二不会(┭┮﹏┭┮):
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 #include<cmath> 6 #include<cstdlib> 7 #include<vector> 8 using namespace std; 9 typedef long long ll; 10 typedef long double ld; 11 typedef pair<int,int> pr; 12 const double pi=acos(-1); 13 #define rep(i,a,n) for(int i=a;i<=n;i++) 14 #define per(i,n,a) for(int i=n;i>=a;i--) 15 #define Rep(i,u) for(int i=head[u];i;i=Next[i]) 16 #define clr(a) memset(a,0,sizeof(a)) 17 #define pb push_back 18 #define mp make_pair 19 #define fi first 20 #define sc second 21 #define pq priority_queue 22 #define pqb priority_queue <int, vector<int>, less<int> > 23 #define pqs priority_queue <int, vector<int>, greater<int> > 24 #define vec vector 25 ld eps=1e-9; 26 ll pp=1000000007; 27 ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;} 28 ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;} 29 void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); } 30 //void add(int x,int y,int z){ v[++e]=y; next[e]=head[x]; head[x]=e; cost[e]=z; } 31 int dx[5]={0,-1,1,0,0},dy[5]={0,0,0,-1,1}; 32 ll read(){ ll ans=0; char last=‘ ‘,ch=getchar(); 33 while(ch<‘0‘ || ch>‘9‘)last=ch,ch=getchar(); 34 while(ch>=‘0‘ && ch<=‘9‘)ans=ans*10+ch-‘0‘,ch=getchar(); 35 if(last==‘-‘)ans=-ans; return ans; 36 } 37 const int N=20; 38 int x[N],y[N],n,m,k; 39 int lx[N],ly[N],rx[N],ry[N],ans=1e09; 40 void dfs(int x_,int y_,int z) 41 { 42 if(z>ans) return; 43 if(x_>n) { ans=z; return; } 44 if(y_<k){ 45 rx[y_+1]=lx[y_+1]=x[x_]; ry[y_+1]=ly[y_+1]=y[x_]; 46 dfs(x_+1,y_+1,z+4); 47 rx[y_+1]=lx[y_+1]=0; ry[y_+1]=ly[y_+1]=0; 48 } 49 int ax,ay,bx,by,nz; 50 for(int i=1;i<=y_;i++) 51 { 52 ax=lx[i]; ay=ly[i]; 53 bx=rx[i]; by=ry[i]; 54 nz=z-2*((bx-ax+1)+(by-ay+1)); 55 lx[i]=min(x[x_],lx[i]); rx[i]=max(x[x_],rx[i]); 56 ly[i]=min(y[x_],ly[i]); ry[i]=max(y[x_],ry[i]); 57 nz+=2*((rx[i]-lx[i]+1)+(ry[i]-ly[i]+1)); 58 dfs(x_+1,y_,nz); 59 lx[i]=ax; ly[i]=ay; 60 rx[i]=bx; ry[i]=by; 61 } 62 } 63 int main() 64 { 65 m=read(),k=read(),n=read(); 66 for(int i=1;i<=n;i++) x[i]=read(),y[i]=read(); 67 dfs(1,0,0); 68 printf("%d\n",ans); 69 return 0; 70 }
20170612测试