C:uva 11691 allergy test
贪心or状压dp
贪心思路:
利用优先队列,每次将最长的两段拿出来,尽可能的互相覆盖后把余下的部分(还能再放段落的部分)压回队列;
代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
priority_queue <int> q ;
int main()
{
ios::sync_with_stdio(false);
int T;
cin >> T;
while(T--)
{
int n;
cin >> n;
while(n--)
{
int x;
cin >> x;
q.push(x);
}
if(n==1)
{
int ans = q.top();
q.pop();
cout << ans << endl;
continue;
}
int ans=0;
while(!q.empty())
{
int a=q.top();
q.pop();
int b=q.top();
q.pop();
if(q.empty())
{
ans+=a+1;//只剩两个说明压完了,因为第一个还没压进去,所以现在压进去
cout << ans << endl;
break;
}
ans+=b;
q.push(a-b+1);//多余的部分再压回去
}
}
return 0;
}
状压:
其实就是暴力,枚举所有可能的排列,删去重复的情况,用二进制数存放状态,仅此而已。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
const int inf = 0x3f3f3f;
const int N=25;
const int M=(1<<20)+5;
int n;
int dp[M][8],num[N];
int dfs(int s,int k) //s表示状态,k表示上一次操作后剩余的过敏源独立存在的时间数(可以用来存放下一个过敏原);
{
if(dp[s][k]!=-1)return dp[s][k];
dp[s][k]=inf;
if(s==(1<<n)-1)return dp[s][k]=0;
int vis[8];
memset(vis,0,sizeof(vis));
for(int i=0;i<n;i++)
{
if(vis[num[i]])continue; //同一种时间长度效果一致
if(s&(1<<i))continue; //已经放过的不放
vis[num[i]]=1;
int t = dfs(s^(1<<i),max(0,num[i]-k-1))+max(1,num[i]-k); //前一个max计算k,后一个max表示放置后的长度。
dp[s][k] = min(dp[s][k],t);
}
return dp[s][k];
}
int main()
{
ios::sync_with_stdio(false);
int T;
cin >> T;
while(T--)
{
cin >> n;
for(int i=0;i<n;i++)cin >> num[i];
memset(dp,-1,sizeof(dp));
cout << dfs(0,0) << endl;
}
return 0;
}
G uva 11695 Flaght Planning
树的直径:两遍dfs即可找到一棵树的直径。
该题的做法是枚举每一条边都删除一次,删除后把两棵子树的中点连起来,再找连起来的整个树的直径,取直径最小的情况。
实际操作不会连中点(太麻烦,我们可以直接算出连中点后的树的直径(=两棵子树最长路径相连)(该题特殊性,删边后应该是只会生成两个子树,而不是还是一棵树,但就算还是一棵树计算方法也是一样的。))
连中点后树的直径=max((d1+1)/2+(d2+1)/2+1,d1,d2);
该题要求输出加边的结点,那么就记录一下路径,直接取路径的中点就好了。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <array>
#include <cstring>
using namespace std;
typedef pair<int, int>PII;
const int inf = 0x3f3f3f;
const int N = 2510;
int cnt, len, fp, mid, fa,vis[N];
int head[N];
array <int, N> path;
struct Edge
{
int from, to, nxt;
bool hs_del;
}edges[2 * N];
void added(int from, int to) //加边
{
Edge& x = edges[cnt];
x.from = from;
x.to = to;
x.nxt = head[from];
x.hs_del = 0;
head[from] = cnt++;
}
void dfs(int st, int step)
{
vis[st] = 1; //不走回头路
path[step] = st; //记录路径
if (step > len)
{
len = step;
fp = st;
mid = path[ len / 2]; //记录路径中点
}
for (int i = head[st]; ~i; i = edges[i].nxt) //用链表的方式来存储和遍历.
{
if (edges[i].hs_del)continue;
int v = edges[i].to;
if (vis[edges[i].to])continue;
dfs(v, step + 1);
}
}
int fun(int st)
{
len = 0;
fp = st;
memset(vis, 0, sizeof(vis));
mid = st;
dfs(st, 0);
int st1 = fp;
len = 0;
memset(vis, 0, sizeof(vis));
dfs(st1, 0);
return len;
}
int main()
{
ios::sync_with_stdio(false);
int T;
scanf("%d", &T);
while (T--)
{
memset(head, -1, sizeof(head));
int n, faa_f, faa_t, fdel_f, fdel_t;
cnt = 0;
int mfnow = 10000000;
scanf("%d", &n);
for (int i = 1; i < n; i++)
{
int f, t;
scanf("%d%d", &f, &t);
added(f, t);
added(t, f);
}
for (int i = 0; i < cnt; i += 2)
{
int fr = edges[i].from, tt = edges[i].to;
edges[i].hs_del = true;
edges[i ^ 1].hs_del = true;
int d1 = fun(fr), fa1 = mid;
int d2 = fun(tt), fa2 = mid;
int d3 = max(d1 / 2, d1 - d1 / 2) + max(d2 / 2, d2 - d2 / 2)+1;
d1 = max(d3, max(d2, d1));
if (d1 < mfnow)
{
mfnow = d1;
fdel_f = edges[i].from;
fdel_t = edges[i].to;
faa_f = fa1;
faa_t = fa2;
}
edges[i].hs_del = false;
edges[i ^ 1].hs_del = false;
}
printf("%d\n%d %d\n%d %d\n", mfnow, fdel_f, fdel_t, faa_f, faa_t);
}
system("pause");
return 0;
}